diff --git a/.buginfo b/.buginfo new file mode 100644 index 0000000..6fa36b8 --- /dev/null +++ b/.buginfo @@ -0,0 +1,4 @@ +system: jira +server: jira.unity3d.com +project: ECSB +issuetype: Bug \ No newline at end of file diff --git a/.footignore b/.footignore new file mode 100644 index 0000000..9cf577b --- /dev/null +++ b/.footignore @@ -0,0 +1 @@ +ValidationExceptions.json diff --git a/.signature b/.signature index f634b9c..7590945 100644 --- a/.signature +++ b/.signature @@ -1,5 +1,5 @@ { - "timestamp": 1691186202, - "signature": "orgNrb50nSC/yL81htPYIx/n/dIJgdgaUT/2X+lpf7zjp6/w5nFhiJQpsoL1tQV/WQvTZvPagdTqVXrMRccwBXMIxg/GEER3A/cArroxl+ti5tTIzN08wwrk2z+pPO03cSCaqLHjt3IkGUeT7p366RouIL98uGIO2jY3eSFJEWZkMyZ1Zp4OoR7mmStvnZyU5HUlbujR28pHrtKGSSLuA3fAKiF212jHD8UkAePwUbsKhSxgp9L7oFT16wWbt3eK8R5Wx3m7tA14978EbeQCOlFybEAhpytxBxK7OcuxPJyJ9SSBZ/vOW/EH4b/bMB5375LZ47SIG6eouuGB/PW69yNzXeGP5Lq6QIE8ZCiVwZDQcqW/3fFmiVb0hloZ3dgenOVG6mAjXSOQ9/DwwfJE7SlkjxFP34lM2IsIIus159dREzwF05E28/ZLBBZjucJpZhLyH8tEYzYjjSnoI0/8ML1vnaRfs+OZoZdo/zqSxDUClmCE7XqDioAnuig9Eh1X", + "timestamp": 1695065981, + "signature": "YCV6QPUtvkYAXfuH8MSnha1DV0VKCUX7pdD+1SqL9NHY76MFodRJFHcVYHQMmSEd8YCcafP/2haJRw9i68/eMBBC+kekI+96VyY5WtUZvwmDGJPRbZke7lCT/UQ3bdFsRaWuUwXfsbJ0aZeyVEu5UMVoTNoCtt1wS2WrUK/gqXn4fGbpM+XoKAzqi8KqWGjqvQZxrw8IS3uAB7bAh8f4xehLekWGE/KY6hhgcYEk+qmOMjW51iM1Tx50Z2c2fMyCOANzVx96iTSSF/aHdDMQKCx/SkzwmHNJwIjVpWW2Ixl7e9MC0LdKe1YXgQKy3Pjp1NwhGpjzP4MDz3XEaZfB3OaGVIGBzikSl1dDQf67wkG0yNRyzfGaTd3If17+yra/vLHYUaLQxkDuiZyQB4vKeaK5ozE2gkMpbWua18gjRsQx5mlMylK0AoiUhvpG6oM3P182EchXHIGDOtCdpdaFUAq6FW2znTi99/IVwFqhsqNOxyljs8heRI2mGwOJxb8/", "publicKey": "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQm9qQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FZOEFNSUlCaWdLQ0FZRUFzdUhXYUhsZ0I1cVF4ZEJjTlJKSAordHR4SmoxcVY1NTdvMlZaRE1XaXhYRVBkRTBEMVFkT1JIRXNSS1RscmplUXlERU83ZlNQS0ZwZ1A3MU5TTnJCCkFHM2NFSU45aHNQVDhOVmllZmdWem5QTkVMenFkVmdEbFhpb2VpUnV6OERKWFgvblpmU1JWKytwbk9ySTRibG4KS0twelJlNW14OTc1SjhxZ1FvRktKT0NNRlpHdkJMR2MxSzZZaEIzOHJFODZCZzgzbUovWjBEYkVmQjBxZm13cgo2ZDVFUXFsd0E5Y3JZT1YyV1VpWXprSnBLNmJZNzRZNmM1TmpBcEFKeGNiaTFOaDlRVEhUcU44N0ZtMDF0R1ZwCjVNd1pXSWZuYVRUemEvTGZLelR5U0pka0tldEZMVGdkYXpMYlpzUEE2aHBSK0FJRTJhc0tLTi84UUk1N3UzU2cKL2xyMnZKS1IvU2l5eEN1Q20vQWJkYnJMbXk0WjlSdm1jMGdpclA4T0lLQWxBRWZ2TzV5Z2hSKy8vd1RpTFlzUQp1SllDM0V2UE16ZGdKUzdGR2FscnFLZzlPTCsxVzROY05yNWdveVdSUUJ0cktKaWlTZEJVWmVxb0RvSUY5NHpCCndGbzJJT1JFdXFqcU51M3diMWZIM3p1dGdtalFra3IxVjJhd3hmcExLWlROQWdNQkFBRT0KLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0tCg" } \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 7af0159..ba7d2bb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,110 @@ # Changelog +## [1.1.0-exp.1] - 2023-09-18 + +### Added + +* Added a "Custom Transform System" folder in the assets folder of the "EntitiesSamples" project. +* missing documentation on search public api +* Enabled model assets to be baked as prefabs using EntityPrefabReference. +* Error DC0084 is generated when capturing a local variable in an Entities.ForEach that isn't used. +* Add GetSharedComponentIndexManaged API +* Added addition errors around improper use of SystemAPI methods with generic type arguments. +* Search Keyword registration for entities preferences and settings. +* `SystemAPI.Query` now supports `WithSharedComponentFilterManaged(T sharedComponent)` and `WithSharedComponentFilterManaged(T1 sharedComponent1, T2 sharedComponent2)`. +* `CompleteDependencyBeforeRW(SystemState state)` and `CompleteDependencyBeforeRO(SystemState state)` are added to the public `Unity.Entities.IAspectCreate` interface in order to faciltiate changes to source-generated code. Implementations of these methods, like all existing methods in `Unity.Entities.IAspectCreate`, will automatically be generated on users' behalf by source generators. +* Added `IBaker.CreateAdditionalEntities` for creating multiple additional entities at once. +* filter to search with SharedComponent from within the Hierarchy Window +* filter to search with SharedComponent from within the Search Window +* New `EntityQuery` component type constraint: `Present` components are required to be present on a query's matching archetypes, whether or not they are enabled or disabled on individual entities. This constraint can be added in all the usual places -- `EntityQueryBuilder.WithPresent()`, `EntityQueryDesc.Present[]`, the `[WithPresent(typeof(T))]` attribute on `IJobEntity`, etc. +* checks to see if an exclusive transaction is active while scheduling a job. +* Specific error when capturing a variable in `Entities.ForEach` that relies on relies on source generators (since there is no deterministic order between source generators, this can be an error). + +### Changed + +* Significantly improved the performance of `EntityManager.SetSharedComponent(EntityQuery,T)` and `EntityManager.SetSharedComponentManaged(EntityQuery,T)` +* `TypeManager.Initialize` will disable synchronous Burst compilation only during initialization of the TypeManager so that large synchronous compilation stalls when compiling function pointers can be avoided when iterating in the Editor. +* BlobBuilder is now partial +* CompanionGameObjectUpdateTransformSystem is now public +* ResetUpdateAllocator is now public +* SubSceneInspectorUtility is now public +* Batch primary entity creation in baking. +* `HasSingleton()` and `TryGetSingleton()` methods will now throw if they find >1 instance of `T`, instead of returning `false`. Having more than one instance of a singleton component indicates a bug in the program, and should fail more obviously. +* The visibility of the `EnabledBitUtility` class was changed from `public` to `internal`. This class was never intended to be part of the public API of the Entities package, and should not be used by application code. +* `EntityManager.AddChunkComponentData(EntityQuery,T)` no longer throws an exception if the component `T` is already present on any of the target chunks. Instead, the new value is assigned to the existing component. This matches the behavior of other AddComponent variants in the Entities package. +* Text for exception that occurs when an entity doesn't exist during EntityCommandBuffer playback. +* `IJobChunk` now allows indexed writes to native containers passed in the job struct. Only writes to the element at `unfilteredChunkIndex` are valid. To disable this check on a per-container basis, add `[NativeDisableParallelForRestriction]` to the relevant field in the job struct. + +### Deprecated + +* Deprecate GetSharedComponentDataIndex as it is a dupplicate of GetSharedComponentIndex + +### Removed + +* Removed gizmo rendering logic for entities from C#, now this is handled natively in Unity. +* Marked `EntityManager.Instantiate(NativeArray srcEntities, NativeArray outputEntities)` as obsolete, with the intention to eventually remove it entirely. + +### Fixed + +* Code fix now available to rewrite offending code that trigger `CS1654` errors. +* Fixed a typo in the "LocalTransform" summary comment. +* Property drawer for arrays and lists of Weak(Object|Scene)Reference types +* Compilation with DISABLE_ENTITIES_JOURNALING works again. +* Ensure Component gizmos are rendered for GameObjects in SubScenes when rendered with Entities. That is when using Preferences->Entities->SceneView Mode -> Runtime Data. +* Added null checks in BlobAssetReferenceData properties to avoid crashing the engine while inspecting variables with the debugger. +* LocalTransform.FromMatrixSafe would throw exceptions for valid matricies +* IJobEntity overwriting files due to colliding filenames. +* Differ discards ChunkComponents added during baking. +* Fixed issue with BakingVersion(true) triggering warnings in the console log about the attribute being missing on the type you placed this on in cases where named BakingVersion attributes are being used in the containing assembly. +* A source generator error is not thrown anymore when using the fully qualified name of `SystemAPI.Time` (e.g. `Unity.Entities.SystemAPI.Time`). +* The main entity in LinkedEntityGroups were not added in incremental baking. +* Fix memory leak in the BakerEntityUsage not disposing properly the list of ReferencedEntityUsage +* Fixed `isReadOnly` being ignored in `EntityManager.GetBuffer`. +* ArgumentException on an unknown type when using the GetComponent API in a baker with an abstract type. +* Subscenes no longer redundantly rebake on recompile due to type order changes. +* Serialization of blob asset references in unmanaged shared components +* `SystemGenerator` in the source-generation solution runs in ~48% less time when tested on a small game project shared by one of our users. +* You now no longer get a compile error for methods containing SystemAPI, EFE, or IJE scheduling, that include a signature with nullables, multiple generics, or parameter modifiers. +* KeyNotFoundException thrown by the Entities.Editor.HierarchyWindow when loading a new gameobject scene +* Users can now specify duplicate components in the same `IJobEntity.Execute()` method, insofar as exactly one of them is wrapped in `EnabledRef`. +* Fixed issue where ambiguous types used in a `SystemAPI.Query()` call would generate a compiler error from source generators. +* Entities Hierarchy: when entering playmode without fast enter playmode the hierarchy was showing the authoring datamode content even though the switch in the window header was showing the mixed datamode. +* Validation for world's existence before accessing within EntityContainer and EntitySelectionProxy. + + +## [1.0.16] - 2023-09-11 + +### Added + +* checks to see if an exclusive transaction is active while scheduling a job. + +### Changed + +* Clarified in TransformHelpers API documentation that matrices are expected to be affine. +* Updated Burst dependency to version 1.8.8 + +### Removed + +* Alignment attribute is removed when displaying component attributes in Inspector window. +* Changelog entry for * Added `EntityCommandBuffer.MoveComponent(Entity src, Entity dst)` this will be added in an upcoming minor version. + +### Fixed + +* Companion objects appearing in the GameObject hierarchy for a frame +* `SystemAPI.Query>()` can now be used with `.WithNone()`, `.WithAny()` and `.WithDisabled()`. +* "AssetDatabase.RegisterCustomDependency are restricted during importing" exception thrown by the EntityClientSetting in the OnDisable method, when using 2023.2 or newer. +* The entities journaling window will no longer show an error when a system handle cannot be resolved; this is not an error and simply means that no system was executing when that event was recorded. +* Search window for Systems now shows results from all the worlds instead of default ones. +* Special handling for prefab entity instantiated from prefab asset at runtime. +* Entities Hierarchy: fix an issue that can happen when copying scene entities from different worlds. +* Entities Hierarchy: Fix memory leak in hierarchy backend. +* Entities Hierarchy: Only remove subscene nodes when they are actually not used anymore. +* Fixed `isReadOnly` being ignored in `EntityManager.GetBuffer`. +* Save unsaved opened scenes in the Editor before building a player. (fix issue: Error building Player: InvalidOperationException: ContentCatalogBuildUtility.BuildContentArchives failed with status 'UnsavedChanges') +* Fixed memory leak in some cases when an `EntityCommandBuffer` containing `DynamicBuffer` commands was disposed before it was played back. +* `World.AddSystemManaged(T system)` no longer throws an exception if the system type `T` is not registered. Instead, it registers the type just in time. This matches the existing behavior of `World.CreateSystemManaged()`. +* Fixed a hash mismatch on DependOnParentTransformHierarchy + ## [1.0.14] - 2023-07-27 @@ -21,7 +126,6 @@ * Increased allocation size in RuntimeContentManager initialization * More informative error message in WriteSceneHeader() - ### Removed * Alignment attribute is removed when displaying component attributes in Inspector window. @@ -175,8 +279,6 @@ ### Fixed -* Allow components to contain NativeContainers whose element type is or contains a NativeContainer. Previously the TypeManager would throw during initialization if a component contained a a nested NativeContainer field. Note: NativeContainers still cannot be verified to be safely accessed when used in jobs. Thus, if a component contains a nested NativeContainer field, that component can only be accessed from the main thread. -* Fixed memory leak in content loading system when scenes are unloaded before fully loading. * Allow components to contain NativeContainers whose element type is or contains a NativeContainer. Previously the TypeManager would throw during initialization if a component contained a a nested NativeContainer field. Note: NativeContainers still cannot be verified to be safely accessed when used in jobs. Thus, if a component contains a nested NativeContainer field, that component can only be accessed from the main thread. * improved error message when `EntityQuery.GetSingleton()` fails * Query window minimum size and scrolling behavior. @@ -321,7 +423,6 @@ * Stripping (e.g. on IL2CPP) now won't strip whole assemblies that have important systems, like graphics. * Generic systems created at runtime no longer break sorting functionality. - ## [1.0.0-pre.44] - 2023-02-13 ### Added diff --git a/DocCodeSamples.Tests/EntityQueryExamples.cs b/DocCodeSamples.Tests/EntityQueryExamples.cs index 54d670e..ed4e5d8 100644 --- a/DocCodeSamples.Tests/EntityQueryExamples.cs +++ b/DocCodeSamples.Tests/EntityQueryExamples.cs @@ -382,18 +382,15 @@ protected override void OnCreate() protected override void OnUpdate() { - // Only iterate over entities that have the SharedGrouping data set to 1 + // By default (without a filter), count all entities that have the required components. + query.ResetFilter(); + int unfilteredCount = query.CalculateEntityCount(); + // With a filter, only entities in chunks that have SharedGrouping=1 will be counted. 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 ObjectPosition - { - Value = positions[i].Value + displacements[i].Value - }; + int filteredCount = query.CalculateEntityCount(); + // Many query methods include a variant that ignores any active filters. These variants are generally + // more efficient, and should be used when conservative upper-bound results are acceptable. + int ignoreFilterCount = query.CalculateEntityCountWithoutFiltering(); } } diff --git a/Documentation~/baking-prefabs.md b/Documentation~/baking-prefabs.md index 1e0ff75..d663b95 100644 --- a/Documentation~/baking-prefabs.md +++ b/Documentation~/baking-prefabs.md @@ -26,6 +26,9 @@ To instantiate prefabs that are referenced in components, use an [`EntityManager [!code-cs[InstantiateEmbeddedPrefabs](../DocCodeSamples.Tests/BakingPrefabExamples.cs#InstantiateEmbeddedPrefabs)] +> [!NOTE] +> Instanced prefabs will contain a [SceneSection component](streaming-scene-sections.md#entity-prefabs-and-sections). This could affect the lifetime of the entity. + To instantiate a prefab referenced with `EntityPrefabReference`, you must also add the [`RequestEntityPrefabLoaded`](xref:Unity.Scenes.RequestEntityPrefabLoaded) struct to the entity. This is because Unity needs to load the prefab before it can use it. `RequestEntityPrefabLoaded` ensures that the prefab is loaded and the result is added to the `PrefabLoadResult` component. Unity adds the [`PrefabLoadResult`](xref:Unity.Scenes.PrefabLoadResult) component to the same entity that contains the `RequestEntityPrefabLoaded`. [!code-cs[InstantiateLoadedPrefabs](../DocCodeSamples.Tests/BakingPrefabExamples.cs#InstantiateLoadedPrefabs)] @@ -47,4 +50,4 @@ To destroy a prefab instance, use an [`EntityManager`](xref:Unity.Entities.Entit ## Additional resources -* [Baker overview](baking-baker-overview.md) \ No newline at end of file +* [Baker overview](baking-baker-overview.md) diff --git a/Documentation~/streaming-scene-sections.md b/Documentation~/streaming-scene-sections.md index 8597066..4cc111d 100644 --- a/Documentation~/streaming-scene-sections.md +++ b/Documentation~/streaming-scene-sections.md @@ -35,6 +35,12 @@ In subscenes, ECS components can only contain references to: >[!IMPORTANT] > References to entities that are in a different section from the component, or aren't in section 0, are set to `Entity.Null` when they're loaded. +## Entity Prefabs and sections + +All the entities in a scene have a SceneSection component that links them to a section in the scene. When that section or the scene is unloaded, all the entities with a matching SceneSection component will be unloaded too. This applies to entity prefabs as well. + +When an entity prefab is instantiated, its SceneSection is added to the instanced entity. This means that unloading a scene will destroy all the prefabs instances associated with it. If this is not the desired behaviour, you can manually remove the SceneSection component from the prefab instances. + ## Section loading You can load or unload individual sections of a scene independently, but section 0 must always load first. Similarly, you can only unload section 0 once all the other sections in the scene are already unloaded. @@ -50,4 +56,4 @@ In a similar way, to unload the content of a section, remove the component [`Uni ## Additional resources * [Baking overview](baking-overview.md) -* [`SceneSectionComponent` API documentation](xref:Unity.Entities.SceneSectionComponent) \ No newline at end of file +* [`SceneSectionComponent` API documentation](xref:Unity.Entities.SceneSectionComponent) diff --git a/Documentation~/systems-looking-up-data.md b/Documentation~/systems-looking-up-data.md index fdff66a..f9b475a 100644 --- a/Documentation~/systems-looking-up-data.md +++ b/Documentation~/systems-looking-up-data.md @@ -4,10 +4,8 @@ uid: accessing-looking-up-data # Look up arbitrary data - The most efficient way to access and change data is to use a [system](concepts-systems.md) with an [entity query](systems-entityquery.md) and a job. This utilizes the CPU resources in the most efficient way, with minimal memory cache misses. It's best practice to use the most efficient, fastest path to perform the bulk of data transformations. However, there are times when you might need to access an arbitrary component of an arbitrary entity at an arbitrary point in your program. - You can look up data in an entity's [`IComponentData`](xref:Unity.Entities.IComponentData) and its [dynamic buffers](components-buffer-introducing.md). The way you look up data depends on whether your code uses [`Entities.ForEach`](xref:Unity.Entities.SystemBase.Entities), or an `IJobChunk` job, or some other method on the main thread to execute in a system. ## Look up entity data in a system diff --git a/Documentation~/transforms-custom.md b/Documentation~/transforms-custom.md index 7d89324..1a8072e 100644 --- a/Documentation~/transforms-custom.md +++ b/Documentation~/transforms-custom.md @@ -1,12 +1,12 @@ # Custom transforms -You can customize the built-in transform system to address the specific transform functionality needs of your project. This section explains how to create a custom transform system, and uses the [2D custom transform system](https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/EntitiesSamples) as a concrete example of one. +You can customize the built-in transform system to address the specific transform functionality needs of your project. This section explains how to create a custom transform system, and uses the [2D custom transform system](https://github.com/Unity-Technologies/EntityComponentSystemSamples/tree/master/EntitiesSamples/Assets/Miscellaneous/CustomTransforms) as a concrete example of one. ## Write groups -[Write groups](systems-write-groups.md) enable you to override the built-in transform system with your own transforms. The built-in transform system uses write groups internally and you can configure them to make the built-in transform system ignore entities that you want your custom transform system to use. +[Write groups](systems-write-groups.md) enable you to override the built-in transform system with your own transforms. The built-in transform system uses write groups internally and you can configure them to make the built-in transform system ignore entities that you want to process with your custom transform system. -More precisely, write groups exclude specific entities from queries. These queries are passed to the jobs that the built-in transform system uses. You can use write groups on certain components to exclude entities with those components from being processed by the jobs of the built-in transform system and instead processed by your own transform system. For more information, refer to the documentation on [write groups](systems-write-groups.md). +More precisely, write groups exclude specific entities from queries. These queries are passed to the jobs that the built-in transform system uses. You can use write groups on certain components to exclude entities with those components from being processed by the jobs of the built-in transform system, and those entities can instead be processed by your own transform systems. For more information, refer to the documentation on [write groups](systems-write-groups.md). ## Create a custom transform system @@ -18,13 +18,13 @@ The following steps outline how to create a custom transform system: ### Substitute the LocalTransform component -The built-in transform system adds a [`LocalTransform`](xref:Unity.Transforms.LocalTransform) component to each entity by default. It stores the data that represents an entity's position, rotation, and scale. There are also a variety of static helper methods defined on it. +The built-in transform system adds a [`LocalTransform`](xref:Unity.Transforms.LocalTransform) component to each entity by default. It stores the data that represents an entity's position, rotation, and scale. There are also a variety of static helper methods defined on this component. To create your own custom transform system, you have to substitute the `LocalTransform` component with your own. 1. Create a .cs file that defines a substitute for the built-in `LocalTransform` component. You can copy the built-in `LocalTransform.cs` file from the Entities package into your assets folder and then edit the contents. To do this, go to **Packages > Entities > Unity.Transforms** in your project, copy the `LocalTransform.cs` file, and rename it. -1. Change the properties and methods to suit your needs. For example: - +1. Change the properties and methods to suit your needs. See the following example of a custom `LocalTransform2D` component: + ```c# using System.Globalization; using Unity.Entities; @@ -32,7 +32,6 @@ To create your own custom transform system, you have to substitute the `LocalTra using Unity.Properties; using Unity.Transforms; - [WriteGroup(typeof(LocalToWorld))] public struct LocalTransform2D : IComponentData { @@ -66,12 +65,12 @@ To create your own custom transform system, you have to substitute the `LocalTra The above example modifies the built-in `LocalTransform` in the following ways: * Adds the `[WriteGroup(typeof(LocalToWorld))]` attribute. -* Reduces the `Position` field from a `float3` to a `float2`. This is because in the 2D sample, entities should only move along the XY plane. -* Reduces the `Rotation` field to a `float` that represents the number of degrees around the z-axis. The built-in transform system's `Rotation` property is a quaternion that represents a rotation in 3D space. +* Reduces the `Position` field from a `float3` to a `float2`. This is because in the 2D sample, entities only move along the XY plane. +* Reduces the `Rotation` field to a `float` that represents the number of degrees of rotation around the z-axis. The built-in transform system's `Rotation` property is a quaternion that represents a rotation in 3D space. * Removed all methods apart from `ToMatrix` and `ToString`. The `ToMatrix` method has been modified to work in 2D. The other methods aren't needed for the custom 2D transform system. > [!NOTE] -> `LocalTransform2D` is in the global namespace. In the sample project it's in a `TransformSystem2D` namespace to ensure that it doesn't interfere with the other samples in the same project. Both options work as long as all the files of the custom transform system are within the same namespace. +> `LocalTransform2D` is in the global namespace. In the above linked sample project it's in a sub-namespace to ensure that it doesn't interfere with the other samples in the same project. Both options work as long as all the files of the custom transform system are within the same namespace. ### Create an authoring component diff --git a/Documentation~/transforms-usage-flags.md b/Documentation~/transforms-usage-flags.md index 1c9feb3..f99490f 100644 --- a/Documentation~/transforms-usage-flags.md +++ b/Documentation~/transforms-usage-flags.md @@ -23,7 +23,10 @@ Similarly, if the window GameObject is on a GameObject that represents a ship, y [!code-cs[Transform usage flags](../DocCodeSamples.Tests/TransformUsageFlagsExamples.cs#ship-example)] +>[!IMPORTANT] +> Entity prefabs are automatically marked as Dynamic so the instances can be placed in the world. + ## Additional resources * [`TransformUsageFlags` API documentation](xref:Unity.Entities.TransformUsageFlags) -* [Baker overview](baking-baker-overview.md) \ No newline at end of file +* [Baker overview](baking-baker-overview.md) diff --git a/LICENSE.md b/LICENSE.md index 3390ada..9fd46b4 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,4 +1,4 @@ -Entities © 2023 Unity Technologies +Entities © 2018 Unity Technologies Licensed under the Unity Companion License for Unity-dependent projects (see https://unity3d.com/legal/licenses/unity_companion_license). diff --git a/ThirdPartyNotices.md b/ThirdPartyNotices.md deleted file mode 100644 index ec6a466..0000000 --- a/ThirdPartyNotices.md +++ /dev/null @@ -1,16 +0,0 @@ -This package contains third-party software components governed by the license(s) indicated below: ---------- - -Component Name: ilspy - -License Type: "MIT" - -Copyright (c) 2011-2021 AlphaSierraPapa for the ILSpy team - -https://github.com/icsharpcode/ILSpy - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/ThirdPartyNotices.md.meta b/ThirdPartyNotices.md.meta deleted file mode 100644 index 8b4f8ce..0000000 --- a/ThirdPartyNotices.md.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 9908e38a8c5d86f45ba098d517777349 -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Burst.Tests/BurstDelegateTest.cs b/Unity.Burst.Tests/BurstDelegateTest.cs index 2b27a2f..95e9a0e 100644 --- a/Unity.Burst.Tests/BurstDelegateTest.cs +++ b/Unity.Burst.Tests/BurstDelegateTest.cs @@ -67,7 +67,7 @@ unsafe public void Execute() } [Test] -#if !UNITY_DOTSRUNTIME && !UNITY_WEBGL +#if !UNITY_WEBGL [ConditionalIgnore("IgnoreForCoverage", "Fails randonly when ran with code coverage enabled")] #endif public void CompileMissingBurstCompile() diff --git a/Unity.Burst.Tests/BurstSafetyTests.cs b/Unity.Burst.Tests/BurstSafetyTests.cs index 1f2e72d..9cc5596 100644 --- a/Unity.Burst.Tests/BurstSafetyTests.cs +++ b/Unity.Burst.Tests/BurstSafetyTests.cs @@ -167,7 +167,6 @@ public void Execute() } #if ENABLE_UNITY_COLLECTIONS_CHECKS -#if !UNITY_DOTSRUNTIME [Test] [Ignore("Crashes Unity - No user is supposed to write code like this, so not very important")] public void AccessNullUnsafePtr() @@ -176,7 +175,5 @@ public void AccessNullUnsafePtr() new AccessNullUnsafePtrJob().Run(); } - -#endif #endif } diff --git a/Unity.Core/Compression/Codec.cs b/Unity.Core/Compression/Codec.cs index 3c50672..b6ffdf3 100644 --- a/Unity.Core/Compression/Codec.cs +++ b/Unity.Core/Compression/Codec.cs @@ -100,13 +100,7 @@ static public unsafe bool Decompress(Codec codec, in byte* compressedData, int c } } -#if !UNITY_DOTSRUNTIME - // We assume when not using DOTS Runtime the liblz4 dll is provided externally for linking const string DllName = "liblz4"; - -#else - const string DllName = "lib_unity_entities"; -#endif [DllImport(DllName, EntryPoint = "LZ4_compressBound")] static extern unsafe int CompressBoundLZ4(int srcSize); [DllImport(DllName, EntryPoint = "LZ4_compress_default")] diff --git a/Unity.Core/Hashes/XXHash.cs b/Unity.Core/Hashes/XXHash.cs index dc43c9e..5f836c7 100644 --- a/Unity.Core/Hashes/XXHash.cs +++ b/Unity.Core/Hashes/XXHash.cs @@ -70,7 +70,6 @@ public static unsafe uint Hash32(byte* buffer, int bufferLength, uint seed = 0) return avalanche32(acc); } -#if !NET_DOTS /// /// Generate a 32-bit xxHash value from a stream. /// @@ -130,8 +129,6 @@ public static unsafe uint Hash32(System.IO.Stream stream, uint seed = 0) return avalanche32(acc); } -#endif - /// /// Generate a 64-bit xxHash value. /// @@ -175,7 +172,6 @@ public static unsafe ulong Hash64(byte* buffer, int bufferLength, ulong seed = 0 return avalanche64(acc); } -#if !NET_DOTS /// /// Generate a 64-bit xxHash value from a stream. /// @@ -240,8 +236,6 @@ public static unsafe ulong Hash64(System.IO.Stream stream, ulong seed = 0) return avalanche64(acc); } -#endif - //[MethodImpl(MethodImplOptions.AggressiveInlining)] //private static unsafe (ulong, ulong, ulong, ulong) initAccumulators64(ulong seed) //{ diff --git a/Unity.Core/TimeData.cs b/Unity.Core/TimeData.cs index 960ccd1..f67eb24 100644 --- a/Unity.Core/TimeData.cs +++ b/Unity.Core/TimeData.cs @@ -36,15 +36,11 @@ public TimeData(double elapsedTime, float deltaTime) DeltaTime = deltaTime; } - #if !UNITY_DOTSRUNTIME - /// /// Currently, an alias to . /// /// This member will be deprecated once a native fixed delta time is introduced in Unity.Entities. [EditorBrowsable(EditorBrowsableState.Never)] public float fixedDeltaTime => UnityEngine.Time.fixedDeltaTime; - - #endif } } diff --git a/Unity.Entities.Build/AssemblyInfo.cs b/Unity.Entities.Build/AssemblyInfo.cs index 2d604b7..a23c095 100644 --- a/Unity.Entities.Build/AssemblyInfo.cs +++ b/Unity.Entities.Build/AssemblyInfo.cs @@ -6,3 +6,5 @@ [assembly: InternalsVisibleTo("Unity.NetCode.Authoring.Hybrid")] [assembly: InternalsVisibleTo("Unity.Entities.Hybrid")] [assembly: InternalsVisibleTo("Unity.Entities.Hybrid.Tests.ExcludedAssembly")] +[assembly: InternalsVisibleTo("Unity.Motion.Hybrid")] +[assembly: InternalsVisibleTo("Unity.Motion")] diff --git a/Unity.Entities.Build/BakingSystemFilterSettings.cs b/Unity.Entities.Build/BakingSystemFilterSettings.cs index 97953d8..dee2de7 100644 --- a/Unity.Entities.Build/BakingSystemFilterSettings.cs +++ b/Unity.Entities.Build/BakingSystemFilterSettings.cs @@ -80,6 +80,7 @@ internal UnityEditorInternal.AssemblyDefinitionAsset FindAssemblyDefinitionAsset return UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); } + //todo: add this attribute to GetSystemAttributes, and then stop using reflection here internal bool ShouldRunBakingSystem(Type type) { UpdateIfDirty(); diff --git a/Unity.Entities.Build/EntitiesClientSettings.cs b/Unity.Entities.Build/EntitiesClientSettings.cs index 8f4cf1c..8d0e9a5 100644 --- a/Unity.Entities.Build/EntitiesClientSettings.cs +++ b/Unity.Entities.Build/EntitiesClientSettings.cs @@ -62,12 +62,41 @@ public string[] GetAdditionalScriptingDefines() internal void Save() { - Save(true); ((IEntitiesPlayerSettings)this).RegisterCustomDependency(); if (!AssetDatabase.IsAssetImportWorkerProcess()) + { + Save(true); AssetDatabase.Refresh(); + } + } + +#if UNITY_2023_2_OR_NEWER + private void OnEnable() + { + ((IEntitiesPlayerSettings)this).RegisterCustomDependency(); + } +#endif + + private void OnDisable() + { +#if !UNITY_2023_2_OR_NEWER + Save(); +#else + //We can't call the RegisterCustomDependency here. We still save the asset (because it must be) + //But the depedency is going to be update when the scriptable is re-enabled. + if (AssetDatabase.IsAssetImportWorkerProcess()) + return; + + Save(true); + //This safeguard is necessary because the RegisterCustomDependency throw exceptions + //if this is called when the editor is refreshing the database. + if(!EditorApplication.isUpdating) + { + ((IEntitiesPlayerSettings)this).RegisterCustomDependency(); + AssetDatabase.Refresh(); + } +#endif } - private void OnDisable() { Save(); } } internal class ClientSettings : DotsPlayerSettingsProvider diff --git a/Unity.Entities.CodeGen/Cloner/Cloner.cs b/Unity.Entities.CodeGen/Cloner/Cloner.cs index 2e72551..ea7664e 100644 --- a/Unity.Entities.CodeGen/Cloner/Cloner.cs +++ b/Unity.Entities.CodeGen/Cloner/Cloner.cs @@ -33,9 +33,9 @@ protected override bool PostProcessImpl(TypeDefinition[] componentSystemTypes) var originalProperty = properties.OriginalLookup[rewrittenProperty.Source]; if (rewrittenProperty.Definition.GetMethod != null) - methods.Rewritten.Add((rewrittenProperty.Definition.GetMethod, originalProperty.GetMethod.Name)); + methods.Rewritten.Add((rewrittenProperty.Definition.GetMethod, GetMethodNameAndParamsAsString(originalProperty.GetMethod))); if (rewrittenProperty.Definition.SetMethod != null) - methods.Rewritten.Add((rewrittenProperty.Definition.SetMethod, originalProperty.SetMethod.Name+"_"+originalProperty.SetMethod.Parameters[0].ParameterType.FullName)); + methods.Rewritten.Add((rewrittenProperty.Definition.SetMethod, GetMethodNameAndParamsAsString(originalProperty.SetMethod))); typeDef.Properties.Remove((((PropertyDefinition Definition, string ConstructorArgument))rewrittenProperty).Definition); madeChange = true; } @@ -63,133 +63,11 @@ protected override bool PostProcessUnmanagedImpl(TypeDefinition[] unmanagedCompo return false; } - // Remove /& characters and `# for type arity - // Also replace Cecil's System.Int32[0...,0...] syntax with more common System.Int32[,] - static string CleanupParameterTypeName(string typeName) - { - typeName = Regex.Replace(typeName, @"System\.Nullable`1<(.*)>", - m => $"{m.Groups[1].Value}?"); - typeName = typeName.Replace("0...", string.Empty) - .Replace('/', '.') - .Replace("&", "") - .Replace(" ", string.Empty); - - int indexOfArityStart = typeName.IndexOf('`'); - - const int NotFound = -1; - - if (indexOfArityStart == NotFound) - return typeName; - - /* - If this is a nested type within a generic type, e.g.: - - class MyGenericClass - { - struct NestedType {} - struct GenericNestedType {} - } - - class MyDerivedClass : MyGenericClass - { - void RunUpdate1(NestedType nestedType) <-- This parameter type will be written as: "MyGenericClass`1.NestedType" - { - Entities.ForEach(() => { }).ScheduleParallel(); - } - void RunUpdate2(GenericNestedType nestedType) <-- This parameter type will be written as: "MyGenericClass`1.GenericNestedType`1." - { - Entities.ForEach(() => { }).ScheduleParallel(); - } - } - - Given the code sample above, we want to output "MyGenericClass.NestedType" for `RunUpdate1()`, - and "MyGenericClass.GenericNestedType" for `RunUpdate2()`. - */ - - int typeParameterNameStart = typeName.IndexOf('<'); - int potentialNestedTypeSeparatorIndex = indexOfArityStart + 2; - - const char nestedTypeSeparator = '.'; - bool isNestedTypeInGenericType = typeName[potentialNestedTypeSeparatorIndex] == nestedTypeSeparator && typeParameterNameStart != NotFound; - - if (isNestedTypeInGenericType) - { - int typeParameterNameEnd = typeName.IndexOf('>'); - - // E.g. from "MyGenericClass`1.GenericNestedType`1.", retrieve ["MyComponent", "YourComponent"] - // From "MyGenericClass`1.NestedType", retrieve ["MyComponent"] - string[] allTypeParameterNames = typeName.Substring(startIndex: typeParameterNameStart + 1, length: typeParameterNameEnd - (typeParameterNameStart + 1)).Split(','); - - int typeParamNamesStartIndex = 0; - var formattedStringBuilder = new StringBuilder(); - - // To visualise, visit https://regex101.com/ or any other Regex debugging site. - // Enter the pattern "[.]*(?[a-zA-Z_.]+)`(?\d+)*|[.]*(?[a-zA-Z]+)(?=<)". - // Enter these test strings: - // 1. Unity.Entities.Tests.ForEachCodegen.ForEachCodegenTests.MyGenericTestBaseSystem_WithNestedGenericType`2.NestedType`1.AnotherNestedType`1. - // 2. Unity.Entities.Tests.ForEachCodegen.ForEachCodegenTests.MyGenericTestBaseSystem_OneType`1.Nested_Type - // 3. Unity.Collections.NativeParallelMultiHashMap`2.ParallelWriter - // Inspect the found matches. - var matches = Regex.Matches(typeName, pattern: @"[.]*(?[a-zA-Z_0-9.]+)`(?\d+)*|[.]*(?[a-zA-Z_0-9]+)(?=<)").ToArray(); - - for (var matchIndex = 0; matchIndex < matches.Length; matchIndex++) - { - var match = matches[matchIndex]; - - int numTypeParameters = 0; - string genericTypeName = default; - string nonGenericTypeName = default; - - foreach (Group group in match.Groups) - switch (group.Name) - { - case "numTypeParameters": - if (int.TryParse(group.Value, out int paramCount)) // Int-parsing will succeed iff we're dealing with a generic type name. - numTypeParameters = paramCount; - break; - case "genericTypeName": - genericTypeName = group.Value; - break; - case "nonGenericTypeName": - nonGenericTypeName = group.Value; - break; - } - - if (matchIndex > 0) - formattedStringBuilder.Append(nestedTypeSeparator); - - if (!string.IsNullOrEmpty(genericTypeName)) - { - formattedStringBuilder.Append(genericTypeName); - formattedStringBuilder.Append('<'); - - for (int i = typeParamNamesStartIndex; i < typeParamNamesStartIndex + numTypeParameters; i++) - { - formattedStringBuilder.Append(allTypeParameterNames[i]); - if (i < typeParamNamesStartIndex + numTypeParameters - 1) - formattedStringBuilder.Append(','); - } - formattedStringBuilder.Append('>'); - typeParamNamesStartIndex += numTypeParameters; - } - - else if (!string.IsNullOrEmpty(nonGenericTypeName)) - formattedStringBuilder.Append($"{nonGenericTypeName}"); - } - - return formattedStringBuilder.ToString(); - } - - return typeParameterNameStart != NotFound ? typeName.Remove(indexOfArityStart, typeParameterNameStart - indexOfArityStart) : typeName; - } - static string GetMethodNameAndParamsAsString(MethodReference method) { var strBuilder = new StringBuilder(); strBuilder.Append(method.Name); - - for (var typeIndex = 0; typeIndex < method.GenericParameters.Count; typeIndex++) - strBuilder.Append($"_T{typeIndex}"); + strBuilder.Append($"_T{method.GenericParameters.Count}"); foreach (var parameter in method.Parameters) { @@ -203,8 +81,7 @@ static string GetMethodNameAndParamsAsString(MethodReference method) strBuilder.Append($"_ref"); } - - strBuilder.Append($"_{CleanupParameterTypeName(parameter.ParameterType.ToString())}"); + strBuilder.Append($"_{parameter.ParameterType}"); } return strBuilder.ToString(); diff --git a/Unity.Entities.CodeGen/ComponentDataPP.cs b/Unity.Entities.CodeGen/ComponentDataPP.cs index 1970f99..d7975c0 100644 --- a/Unity.Entities.CodeGen/ComponentDataPP.cs +++ b/Unity.Entities.CodeGen/ComponentDataPP.cs @@ -192,15 +192,6 @@ protected override bool PostProcessImpl(TypeDefinition[] componentSystemTypes) memo.m_BurstCompileBits |= 1 << i; } -#if UNITY_DOTSRUNTIME - // Burst CompileFunctionPointer in DOTS Runtime will not currently decorate methods as [MonoPInvokeCallback] - // so we add that here until that is supported - var monoPInvokeCallbackAttributeConstructor = - typeof(Jobs.MonoPInvokeCallbackAttribute).GetConstructor(Type.EmptyTypes); - methodDef.CustomAttributes.Add( - new CustomAttribute(mod.ImportReference(monoPInvokeCallbackAttributeConstructor))); -#else - // Adding MonoPInvokeCallbackAttribute needed for IL2CPP to work when burst is disabled var monoPInvokeCallbackAttributeConstructors = typeof(MonoPInvokeCallbackAttribute).GetConstructors(BindingFlags.Public | @@ -212,7 +203,6 @@ protected override bool PostProcessImpl(TypeDefinition[] componentSystemTypes) methodDef.CustomAttributes.Add(monoPInvokeCallbackAttribute); methodDef.CustomAttributes.Add(new CustomAttribute(preserveAttrCtor)); -#endif var processor = methodDef.Body.GetILProcessor(); diff --git a/Unity.Entities.CodeGen/ISystemPostProcessor.cs b/Unity.Entities.CodeGen/ISystemPostProcessor.cs index b374bfa..18cf602 100644 --- a/Unity.Entities.CodeGen/ISystemPostProcessor.cs +++ b/Unity.Entities.CodeGen/ISystemPostProcessor.cs @@ -184,6 +184,7 @@ private TypeMemo AddStaticForwarders(TypeDefinition systemType, TypeReference sp { wrapperName = specializedSystemType.FullName.Replace('/', '_') + .Replace(',', '_') .Replace('`', '_') .Replace('.', '_') .Replace('<', '_') @@ -220,20 +221,12 @@ private TypeMemo AddStaticForwarders(TypeDefinition systemType, TypeReference sp memo.m_BurstCompileBits |= 1 << i; } -#if UNITY_DOTSRUNTIME - // Burst CompileFunctionPointer in DOTS Runtime will not currently decorate methods as [MonoPInvokeCallback] - // so we add that here until that is supported - var monoPInvokeCallbackAttributeConstructor = typeof(Jobs.MonoPInvokeCallbackAttribute).GetConstructor(Type.EmptyTypes); - _methodDef.CustomAttributes.Add(new CustomAttribute(mod.ImportReference(monoPInvokeCallbackAttributeConstructor))); -#else // Adding MonoPInvokeCallbackAttribute needed for IL2CPP to work when burst is disabled var monoPInvokeCallbackAttributeConstructors = typeof(MonoPInvokeCallbackAttribute).GetConstructors(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance); var monoPInvokeCallbackAttribute = new CustomAttribute(mod.ImportReference(monoPInvokeCallbackAttributeConstructors[0])); monoPInvokeCallbackAttribute.ConstructorArguments.Add(new CustomAttributeArgument(mod.ImportReference(typeof(Type)), mod.ImportReference(typeof(SystemBaseDelegates.Function)))); _methodDef.CustomAttributes.Add(monoPInvokeCallbackAttribute); -#endif - var processor = _methodDef.Body.GetILProcessor(); @@ -257,7 +250,6 @@ private void AddRegistrationCode(List memos) var funcDef = new MethodDefinition("EarlyInit", MethodAttributes.Static | MethodAttributes.Public | MethodAttributes.HideBySig, AssemblyDefinition.MainModule.ImportReference(typeof(void))); funcDef.Body.InitLocals = false; -#if !UNITY_DOTSRUNTIME if (!Defines.Contains("UNITY_EDITOR")) { // Needs to run automatically in the player, but we need to @@ -269,14 +261,12 @@ private void AddRegistrationCode(List memos) attribute.ConstructorArguments.Add(new CustomAttributeArgument(loadTypeEnumType, UnityEngine.RuntimeInitializeLoadType.AfterAssembliesLoaded)); funcDef.CustomAttributes.Add(attribute); } - - if (Defines.Contains("UNITY_EDITOR")) + else { // Needs to run automatically in the editor. var attributeCtor2 = AssemblyDefinition.MainModule.ImportReference(typeof(UnityEditor.InitializeOnLoadMethodAttribute).GetConstructor(Type.EmptyTypes)); funcDef.CustomAttributes.Add(new CustomAttribute(attributeCtor2)); } -#endif _registrationClassDef.Methods.Add(funcDef); diff --git a/Unity.Entities.CodeGen/UserError.cs b/Unity.Entities.CodeGen/UserError.cs index 1f95c78..b0835aa 100644 --- a/Unity.Entities.CodeGen/UserError.cs +++ b/Unity.Entities.CodeGen/UserError.cs @@ -1,7 +1,5 @@ using System; -#if !UNITY_DOTSRUNTIME using System.IO; -#endif using Mono.Cecil; using Mono.Cecil.Cil; using Unity.CompilationPipeline.Common.Diagnostics; @@ -28,12 +26,8 @@ static DiagnosticMessage MakeInternal(DiagnosticType type, string errorCode, str result.File = seq.Document.Url; result.Column = seq.StartColumn; result.Line = seq.StartLine; -#if !UNITY_DOTSRUNTIME var shortenedFilePath = seq.Document.Url.Replace($"{Environment.CurrentDirectory}{Path.DirectorySeparatorChar}", ""); result.MessageData = $"{shortenedFilePath}({seq.StartLine},{seq.StartColumn}): {messageData}"; -#else - result.MessageData = messageData; -#endif } else { diff --git a/Unity.Entities.Editor.PerformanceTests/Hierarchy/DataDiffers/ComponentDataDifferPerformanceTests.cs b/Unity.Entities.Editor.PerformanceTests/Hierarchy/DataDiffers/ComponentDataDifferPerformanceTests.cs index eae67c7..afeaf18 100644 --- a/Unity.Entities.Editor.PerformanceTests/Hierarchy/DataDiffers/ComponentDataDifferPerformanceTests.cs +++ b/Unity.Entities.Editor.PerformanceTests/Hierarchy/DataDiffers/ComponentDataDifferPerformanceTests.cs @@ -7,7 +7,7 @@ namespace Unity.Entities.Editor.PerformanceTests { [TestFixture] [Category(Categories.Performance)] - class ComponentDataDifferPerformanceTests : DifferTestFixture + unsafe class ComponentDataDifferPerformanceTests : DifferTestFixture { [Test, Performance] public void ComponentDataDiffer_Spawn_PerformanceTest([Values(1000, 10_000, 100_000, 250_000, 500_000)] @@ -16,6 +16,7 @@ public void ComponentDataDiffer_Spawn_PerformanceTest([Values(1000, 10_000, 100_ var entities = CreateEntitiesWithMockSharedComponentData(entityCount, World.UpdateAllocator.ToAllocator, i => i % 100, typeof(EcsTestData), typeof(EcsTestSharedComp)); var sharedComponentCount = World.EntityManager.GetSharedComponentCount(); var query = World.EntityManager.CreateEntityQuery(typeof(EcsTestData)); + var ecs = World.EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore; ComponentDataDiffer componentDiffer = null; Measure.Method(() => @@ -26,7 +27,7 @@ public void ComponentDataDiffer_Spawn_PerformanceTest([Values(1000, 10_000, 100_ }) .SetUp(() => { - componentDiffer = new ComponentDataDiffer(typeof(EcsTestData)); + componentDiffer = new ComponentDataDiffer(ecs, typeof(EcsTestData)); }) .CleanUp(() => { @@ -48,7 +49,8 @@ public void ComponentDataDiffer_NoChange_PerformanceTest([Values(0, 1000, 10_000 var entities = CreateEntitiesWithMockSharedComponentData(entityCount, World.UpdateAllocator.ToAllocator, i => i % 100, typeof(EcsTestData), typeof(EcsTestSharedComp)); var sharedComponentCount = World.EntityManager.GetSharedComponentCount(); var query = World.EntityManager.CreateEntityQuery(typeof(EcsTestData)); - var componentDiffer = new ComponentDataDiffer(typeof(EcsTestData)); + var ecs = World.EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore; + var componentDiffer = new ComponentDataDiffer(ecs, typeof(EcsTestData)); Measure.Method(() => { @@ -74,7 +76,8 @@ public unsafe void ComponentDataDiffer_Change_PerformanceTest([Values(1000, 10_0 { var entities = CreateEntitiesWithMockSharedComponentData(entityCount, World.UpdateAllocator.ToAllocator, typeof(EcsTestData)); var query = World.EntityManager.CreateEntityQuery(typeof(EcsTestData)); - var componentDiffer = new ComponentDataDiffer(typeof(EcsTestData)); + var ecs = World.EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore; + var componentDiffer = new ComponentDataDiffer(ecs, typeof(EcsTestData)); var counter = entities.Length; if (changeCount > entityCount) changeCount = entityCount; diff --git a/Unity.Entities.Editor.Tests/EntityContainerTest.cs b/Unity.Entities.Editor.Tests/EntityContainerTest.cs index a55dd6b..725876b 100644 --- a/Unity.Entities.Editor.Tests/EntityContainerTest.cs +++ b/Unity.Entities.Editor.Tests/EntityContainerTest.cs @@ -339,7 +339,7 @@ public void EntityContainer_WhenVisited_ReturnsTheCorrectValuesForAllComponentTy m_Manager.SetComponentData(entity, new ClassComponentData { Category = Category.ClassData }); #endif m_Manager.SetComponentData(entity, new StructComponentData { Category = Category.StructData }); - PropertyContainer.Accept(new TestComponentCategoryVisitor { GameObject = _gameObject}, new EntityContainer(m_Manager, entity, true)); + PropertyContainer.Accept(new TestComponentCategoryVisitor { GameObject = _gameObject}, new EntityContainer(m_Manager.World, entity, true)); } [Test] @@ -372,27 +372,27 @@ public void EntityContainer_WhenVisited_CanReadAndWriteData() #endif m_Manager.SetComponentData(entity, new StructComponentData { Category = Category.StructData }); - PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.StructChunkData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.StructChunkData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.ClassChunkData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.ClassChunkData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.StructData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.StructData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.ClassData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.ClassData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.SharedData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.SharedData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.BufferData), new EntityContainer(m_Manager, entity, false)); - PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.BufferData), new EntityContainer(m_Manager, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.StructChunkData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.StructChunkData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.ClassChunkData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.ClassChunkData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.StructData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.StructData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.ClassData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.ClassData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.SharedData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.SharedData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(false, 25, Category.BufferData), new EntityContainer(m_Manager.World, entity, false)); + PropertyContainer.Accept(new TestDataWriteBackVisitor(true, 25, Category.BufferData), new EntityContainer(m_Manager.World, entity, false)); } [Test] public void EntityContainer_WhenVisitingAnInvalidEntity_DoesNotThrow() { - Assert.DoesNotThrow(() => PropertyContainer.Accept(new InvalidEntityVisitor(), new EntityContainer(m_Manager, Entity.Null, false))); + Assert.DoesNotThrow(() => PropertyContainer.Accept(new InvalidEntityVisitor(), new EntityContainer(m_Manager.World, Entity.Null, false))); var entity = m_Manager.CreateEntity(typeof(StructComponentData), typeof(BufferElement)); - var container = new EntityContainer(m_Manager, entity, false); + var container = new EntityContainer(m_Manager.World, entity, false); // Validate that we are actually visiting something. Assert.Throws(() => PropertyContainer.Accept(new InvalidEntityVisitor(), container)); @@ -419,7 +419,7 @@ public void DynamicBufferContainer_WhenAccessCountOnStaleContainer_DoesNotThrow( buffer.Add(new BufferElement { Category = Category.BufferData, FloatValue = i }); var visitor = new TestDynamicBufferContainerVisitor(); - PropertyContainer.Accept(visitor, new EntityContainer(m_Manager, entity, true)); + PropertyContainer.Accept(visitor, new EntityContainer(m_Manager.World, entity, true)); Assert.That(visitor.Container.Count, Is.EqualTo(50)); diff --git a/Unity.Entities.Editor.Tests/GUI/WeakReferencePropertyDrawerTests.cs b/Unity.Entities.Editor.Tests/GUI/WeakReferencePropertyDrawerTests.cs index 1047d22..c07e29c 100644 --- a/Unity.Entities.Editor.Tests/GUI/WeakReferencePropertyDrawerTests.cs +++ b/Unity.Entities.Editor.Tests/GUI/WeakReferencePropertyDrawerTests.cs @@ -1,4 +1,4 @@ -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS using NUnit.Framework; using UnityEngine; using System.Collections.Generic; diff --git a/Unity.Entities.Editor.Tests/Hierarchy/HierarchyTests.cs b/Unity.Entities.Editor.Tests/Hierarchy/HierarchyTests.cs index c65885d..4db7cf1 100644 --- a/Unity.Entities.Editor.Tests/Hierarchy/HierarchyTests.cs +++ b/Unity.Entities.Editor.Tests/Hierarchy/HierarchyTests.cs @@ -1,4 +1,4 @@ -using NUnit.Framework; +using NUnit.Framework; using Unity.Collections; using Unity.Transforms; using UnityEditor; @@ -10,7 +10,7 @@ sealed class HierarchyTests World m_World; Hierarchy m_Hierarchy; - + [SetUp] public void SetUp() { @@ -29,20 +29,33 @@ public void TearDown() { m_World.Dispose(); m_World = null; - + m_Hierarchy.Dispose(); m_Hierarchy = null; } - + [Test] public void Update_WhenParentIsNull_DoesNotThrow() { m_World.EntityManager.CreateEntity(ComponentType.ReadOnly()); - + + Assert.DoesNotThrow(() => + { + m_Hierarchy.Update(true); + }); + } + + [Test] + public void SetWorldAndUpdate_InExclusiveTransaction_DoesNotThrow() + { + var world = new World("Transaction World"); + world.EntityManager.BeginExclusiveEntityTransaction(); Assert.DoesNotThrow(() => { + m_Hierarchy.SetWorld(world); m_Hierarchy.Update(true); }); + world.Dispose(); } } -} \ No newline at end of file +} diff --git a/Unity.Entities.Editor.Tests/Hierarchy/Model/ChangeTracking/ComponentDataDifferTests.cs b/Unity.Entities.Editor.Tests/Hierarchy/Model/ChangeTracking/ComponentDataDifferTests.cs index d1dff6c..cc61acb 100644 --- a/Unity.Entities.Editor.Tests/Hierarchy/Model/ChangeTracking/ComponentDataDifferTests.cs +++ b/Unity.Entities.Editor.Tests/Hierarchy/Model/ChangeTracking/ComponentDataDifferTests.cs @@ -5,7 +5,7 @@ namespace Unity.Entities.Editor.Tests { - class ComponentDataDifferTests + unsafe class ComponentDataDifferTests { World m_World; NativeList m_NewEntities; @@ -24,7 +24,8 @@ public void Setup() m_MissingEntities = new NativeList(m_World.UpdateAllocator.ToAllocator); m_Storage = new NativeList(m_World.UpdateAllocator.ToAllocator); m_EntityDiffer = new EntityDiffer(m_World); - m_ChunkDiffer = new ComponentDataDiffer(typeof(EcsTestData)); + var ecs = m_World.EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore; + m_ChunkDiffer = new ComponentDataDiffer(ecs, typeof(EcsTestData)); } [TearDown] @@ -259,11 +260,11 @@ public unsafe void ComponentDataDiffer_ExtractSimpleResults() } [Test] - public unsafe void ComponentDataDiffer_DetectMissingChunk() + public void ComponentDataDiffer_DetectMissingChunk() { var entityA = CreateEntity(new EcsTestData { value = 12 }); var entityInChunk = m_World.EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore->GetEntityInChunk(entityA); - Assert.That(entityInChunk.Chunk != null); + Assert.That(entityInChunk.Chunk != ChunkIndex.Null); using (m_ChunkDiffer.GatherComponentChangesAsync(m_World.EntityManager.UniversalQuery, World.UpdateAllocator.ToAllocator, out var jobHandle)) { @@ -274,7 +275,7 @@ public unsafe void ComponentDataDiffer_DetectMissingChunk() m_World.EntityManager.DestroyEntity(entityA); entityInChunk = m_World.EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore->GetEntityInChunk(entityA); - Assert.That(entityInChunk.Chunk == null); + Assert.That(entityInChunk.Chunk == ChunkIndex.Null); using (var result = m_ChunkDiffer.GatherComponentChangesAsync(m_World.EntityManager.UniversalQuery, World.UpdateAllocator.ToAllocator, out var jobHandle)) { diff --git a/Unity.Entities.Editor.Tests/Hierarchy/Model/SubSceneMapTests.cs b/Unity.Entities.Editor.Tests/Hierarchy/Model/SubSceneMapTests.cs new file mode 100644 index 0000000..268de52 --- /dev/null +++ b/Unity.Entities.Editor.Tests/Hierarchy/Model/SubSceneMapTests.cs @@ -0,0 +1,33 @@ +using System; +using NUnit.Framework; +using Unity.Collections; + +namespace Unity.Entities.Editor.Tests +{ + [TestFixture] + class SubSceneMapTests + { + [Test] + public void SubSceneMap_IntegrateChanges_DoesNotThrow_WhenIntegratingSceneTagsReferencingDifferentWorlds() + { + using var nodeStore = new HierarchyNodeStore(Allocator.TempJob); + using var nameStore = new HierarchyNameStore(Allocator.TempJob); + using var map = new SubSceneMap(); + using var world1 = new World("World1"); + using var world2 = new World("World2"); + + var sceneReference1 = new SceneReference { SceneGUID = new Hash128(Guid.NewGuid().ToString("N")) }; + var sceneEntity1 = world1.EntityManager.CreateEntity(); + world1.EntityManager.AddComponentData(sceneEntity1, sceneReference1); + var sceneReference2 = new SceneReference { SceneGUID = new Hash128(Guid.NewGuid().ToString("N")) }; + var sceneEntity2 = world2.EntityManager.CreateEntity(); + world2.EntityManager.AddComponentData(sceneEntity2, sceneReference2); + + using var changes = new SubSceneChangeTracker.SubSceneMapChanges(1024, Allocator.Temp); + changes.CreatedSceneTags.Add(new SceneTag { SceneEntity = sceneEntity1 }); + changes.CreatedSceneTags.Add(new SceneTag { SceneEntity = sceneEntity2 }); + + Assert.DoesNotThrow(() => map.IntegrateChanges(world1, nodeStore, nameStore, changes)); + } + } +} diff --git a/Unity.Entities.Editor.Tests/Hierarchy/Model/SubSceneMapTests.cs.meta b/Unity.Entities.Editor.Tests/Hierarchy/Model/SubSceneMapTests.cs.meta new file mode 100644 index 0000000..a3cc700 --- /dev/null +++ b/Unity.Entities.Editor.Tests/Hierarchy/Model/SubSceneMapTests.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 80705d01d8674452bb9a9beab4b2d4b7 +timeCreated: 1691531249 \ No newline at end of file diff --git a/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleWindowIntegrationTests.TreeViewFoldingStateTests.cs b/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleWindowIntegrationTests.TreeViewFoldingStateTests.cs index 48ac654..adcf311 100644 --- a/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleWindowIntegrationTests.TreeViewFoldingStateTests.cs +++ b/Unity.Entities.Editor.Tests/SystemSchedule/SystemScheduleWindowIntegrationTests.TreeViewFoldingStateTests.cs @@ -19,7 +19,6 @@ partial class SystemScheduleWindowIntegrationTests }; [UnityTest] - [Ignore("Random crashes - https://jira.unity3d.com/browse/DOTSE-1821")] public IEnumerator SystemScheduleWindow_TreeViewFoldingState_PlayToEditorMode() { yield return new EnterPlayMode(); @@ -53,7 +52,6 @@ public IEnumerator SystemScheduleWindow_TreeViewFoldingState_PlayToEditorMode() } [UnityTest] - [Ignore("Random crashes - https://jira.unity3d.com/browse/DOTSE-1821")] public IEnumerator SystemScheduleWindow_TreeViewFoldingState_EditorToPlayMode() { // Editor mode diff --git a/Unity.Entities.Editor/AssemblyInfo.cs b/Unity.Entities.Editor/AssemblyInfo.cs index 9656915..8ac2c12 100644 --- a/Unity.Entities.Editor/AssemblyInfo.cs +++ b/Unity.Entities.Editor/AssemblyInfo.cs @@ -4,3 +4,4 @@ [assembly: InternalsVisibleTo("Unity.Entities.Editor.PerformanceTests")] [assembly: InternalsVisibleTo("Unity.NetCode.Editor")] [assembly: InternalsVisibleTo("Unity.Environment.Editor")] +[assembly: InternalsVisibleTo("Unity.Physics.Editor")] diff --git a/Unity.Entities.Editor/Baking/BakingPreferences.cs b/Unity.Entities.Editor/Baking/BakingPreferences.cs index 9f2bc27..8c384d1 100644 --- a/Unity.Entities.Editor/Baking/BakingPreferences.cs +++ b/Unity.Entities.Editor/Baking/BakingPreferences.cs @@ -58,6 +58,11 @@ public void OnSettingChanged(PropertyPath path) } + public string[] GetSearchKeywords() + { + return ISetting.GetSearchKeywordsFromType(GetType()); + } + class Inspector : PropertyInspector { public override VisualElement Build() diff --git a/Unity.Entities.Editor/Baking/EntityBakingPreview.cs b/Unity.Entities.Editor/Baking/EntityBakingPreview.cs index 719f729..81ebd3e 100644 --- a/Unity.Entities.Editor/Baking/EntityBakingPreview.cs +++ b/Unity.Entities.Editor/Baking/EntityBakingPreview.cs @@ -504,13 +504,13 @@ static void GetInspectorTargets(IReadOnlyList bakingDataEntrie // @TODO (UX) Figure out how we want to show additional entities during multi-selection. if (bakingDataEntries.Count == 1 && state.ShowAdditionalEntities && state.AdditionalEntityIndex != -1) { - result.Add(new EntityContainer(root.EntityManager, root.AdditionalEntities[state.AdditionalEntityIndex])); + result.Add(new EntityContainer(root.EntityManager.World, root.AdditionalEntities[state.AdditionalEntityIndex])); return; } foreach (var data in bakingDataEntries) { - result.Add(new EntityContainer(data.EntityManager, data.PrimaryEntity)); + result.Add(new EntityContainer(data.EntityManager.World, data.PrimaryEntity)); } } @@ -527,7 +527,7 @@ void GetCommonComponentTypes(IReadOnlyList targets, List targets, List targets, IEnu var chunk = world.EntityManager.GetChunk(m_LastBakingData.PrimaryEntity); - if (null == chunk.m_Chunk || chunk != m_LastChunk) + if (ChunkIndex.Null == chunk.m_Chunk || chunk != m_LastChunk) { m_LastChunk = chunk; return true; } + var ecs = world.EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore; + var archetype = ecs->GetArchetype(m_LastChunk.m_Chunk); foreach (var typeIndex in selectedComponentTypes) { - var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(m_LastChunk.m_Chunk->Archetype, typeIndex); + var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(archetype, typeIndex); if (typeIndexInArchetype == -1) continue; - var typeChangeVersion = m_LastChunk.m_Chunk->GetChangeVersion(typeIndexInArchetype); + var typeChangeVersion = archetype->Chunks.GetChangeVersion(typeIndexInArchetype, m_LastChunk.m_Chunk.ListIndex); if (ChangeVersionUtility.DidChange(typeChangeVersion, m_LastGlobalSystemVersion)) { diff --git a/Unity.Entities.Editor/Common/DOTSEditorPreference.cs b/Unity.Entities.Editor/Common/DOTSEditorPreference.cs index 207b0b6..7fda769 100644 --- a/Unity.Entities.Editor/Common/DOTSEditorPreference.cs +++ b/Unity.Entities.Editor/Common/DOTSEditorPreference.cs @@ -11,5 +11,10 @@ class AdvancedSettings : ISetting public void OnSettingChanged(PropertyPath path) { } + + public string[] GetSearchKeywords() + { + return ISetting.GetSearchKeywordsFromType(GetType()); + } } } diff --git a/Unity.Entities.Editor/Decompiler.cs b/Unity.Entities.Editor/Decompiler.cs deleted file mode 100644 index 6e229c4..0000000 --- a/Unity.Entities.Editor/Decompiler.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Text; -using Mono.Cecil; -using Mono.Cecil.Cil; -using UnityEditor; - -[assembly: InternalsVisibleTo("Unity.Entities.CodeGen.Tests")] -namespace Unity.Entities.Editor -{ - enum DecompiledLanguage - { - CSharpOnly, - ILOnly, - CSharpAndIL - } - - static class Decompiler - { - public static Process DecompileIntoCSharp(string fullyQualifiedTypeName, string fullDllPath) - { - StringBuilder referencePaths = GetAllReferencePaths(); - - string executionPath = default; - string arguments = default; - string ilSpyPath = Path.GetFullPath("Packages\\com.unity.entities\\Unity.Entities.Editor\\DOTSCompiler\\.ilspyfolder\\ilspycmd.exe"); - - switch (Environment.OSVersion.Platform) - { - case PlatformID.Win32Windows: - case PlatformID.Win32NT: - { - executionPath = ilSpyPath; - arguments = $"\"{fullDllPath}\" -t \"{fullyQualifiedTypeName}\" {referencePaths}"; - break; - } - case PlatformID.MacOSX: - case PlatformID.Unix: - { - executionPath = $"{EditorApplication.applicationContentsPath}/MonoBleedingEdge/bin/mono"; - arguments = $"{ilSpyPath} \"{fullDllPath}\" -t \"{fullyQualifiedTypeName}\" {referencePaths}"; - break; - } - } - - Process outputCSharpProcess = - new Process - { - StartInfo = new ProcessStartInfo - { - UseShellExecute = false, - CreateNoWindow = true, - FileName = executionPath, - Arguments = arguments, - RedirectStandardOutput = true - } - }; - - outputCSharpProcess.Start(); - return outputCSharpProcess; - } - - static StringBuilder GetAllReferencePaths() - { - var referencePaths = new StringBuilder(); - var processed = new HashSet(); - - foreach (Assembly assembly in - AppDomain.CurrentDomain.GetAssemblies().Where(assembly => !assembly.IsDynamic && !string.IsNullOrEmpty(assembly.Location))) - { - string path; - - try - { - path = Path.GetDirectoryName(assembly.Location); - } - - catch (ArgumentException) - { - Debug.Log($"Unexpected path: {assembly.Location}"); - continue; - } - - if (processed.Contains(path)) - { - continue; - } - processed.Add(path); - referencePaths.Append($"--referencepath \"{path}\" "); - } - - return referencePaths; - } - - public static (Process DecompileIntoCSharpProcess, Process DecompileIntoILProcess) - StartDecompilationProcesses(TypeReference typeReference, DecompiledLanguage decompiledLanguage) - { - var assemblyDefinition = typeReference.Module.Assembly; - - var tempFolder = Path.GetTempPath(); - var fileName = $@"{tempFolder}TestAssembly.dll"; - var fileNamePdb = $@"{tempFolder}TestAssembly.pdb"; - var peStream = new FileStream(fileName, FileMode.Create); - var symbolStream = new FileStream(fileNamePdb, FileMode.Create); - - assemblyDefinition.Write( - peStream, - new WriterParameters - { - SymbolStream = symbolStream, - SymbolWriterProvider = new PortablePdbWriterProvider(), - WriteSymbols = true - }); - - peStream.Close(); - symbolStream.Close(); - - StringBuilder referencePaths = GetAllReferencePaths(); - - var isWin = Environment.OSVersion.Platform == PlatformID.Win32Windows || Environment.OSVersion.Platform == PlatformID.Win32NT; - var ilspycmd = Path.GetFullPath("Packages/com.unity.entities/Unity.Entities.Editor/DOTSCompiler/.ilspyfolder/ilspycmd.exe"); - if (isWin) - ilspycmd = ilspycmd.Replace("/", "\\"); - - string ilSpyArgument = $"{(isWin ? "" : ilspycmd)} \"{fileName}\" -t \"{typeReference.FullName.Replace("/","+")}\" {referencePaths}"; - - var outputCSharpProcess = - decompiledLanguage == DecompiledLanguage.CSharpOnly || decompiledLanguage == DecompiledLanguage.CSharpAndIL - ? new Process - { - StartInfo = new ProcessStartInfo - { - UseShellExecute = false, - CreateNoWindow = true, - FileName = isWin - ? ilspycmd - : $"{EditorApplication.applicationPath}/Contents/MonoBleedingEdge/bin/mono", - Arguments = ilSpyArgument, - RedirectStandardOutput = true - } - } - : null; - - var outputIlCodeProcess = - decompiledLanguage == DecompiledLanguage.ILOnly || decompiledLanguage == DecompiledLanguage.CSharpAndIL - ? new Process - { - StartInfo = new ProcessStartInfo - { - UseShellExecute = false, - CreateNoWindow = true, - FileName = isWin - ? ilspycmd - : $"{EditorApplication.applicationPath}/Contents/MonoBleedingEdge/bin/mono", - Arguments = $"{ilSpyArgument} -il", - RedirectStandardOutput = true - } - } - : null; - - outputCSharpProcess?.Start(); - outputIlCodeProcess?.Start(); - - return (outputCSharpProcess, outputIlCodeProcess); - } - - public static (string CSharpCode, string ILCode) DecompileIntoCSharpAndIL(TypeReference typeReference, DecompiledLanguage decompiledLanguage) - { - var(decompileIntoCSharpProcess, decompileIntoIlProcess) = StartDecompilationProcesses(typeReference, decompiledLanguage); - return (decompileIntoCSharpProcess?.StandardOutput.ReadToEnd(), decompileIntoIlProcess?.StandardOutput.ReadToEnd()); - } - } -} diff --git a/Unity.Entities.Editor/Decompiler.cs.meta b/Unity.Entities.Editor/Decompiler.cs.meta deleted file mode 100644 index 3486b3d..0000000 --- a/Unity.Entities.Editor/Decompiler.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: c89ba35e3566a76448764de5391c262b -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Editor/EnableEntityNames.cs b/Unity.Entities.Editor/EnableEntityNames.cs deleted file mode 100644 index ef7a510..0000000 --- a/Unity.Entities.Editor/EnableEntityNames.cs +++ /dev/null @@ -1,9 +0,0 @@ -#if USING_PLATFORMS_PACKAGE -using Unity.Build; - -namespace Unity.Entities.Editor -{ - class EnableEntityNames : IBuildComponent - {} -} -#endif diff --git a/Unity.Entities.Editor/EnableEntityNames.cs.meta b/Unity.Entities.Editor/EnableEntityNames.cs.meta deleted file mode 100644 index cfa0c11..0000000 --- a/Unity.Entities.Editor/EnableEntityNames.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 5337a61158114b49a7cf0c8dbfd813be -timeCreated: 1614122422 \ No newline at end of file diff --git a/Unity.Entities.Editor/EntityNameBuildPipelineCustomizer.cs b/Unity.Entities.Editor/EntityNameBuildPipelineCustomizer.cs deleted file mode 100644 index 3adbde7..0000000 --- a/Unity.Entities.Editor/EntityNameBuildPipelineCustomizer.cs +++ /dev/null @@ -1,32 +0,0 @@ -#if USING_PLATFORMS_PACKAGE -using System; -using Unity.Build; -using Unity.Build.Classic; - -namespace Unity.Entities.Editor -{ - class EntityNameBuildPipelineCustomizer: ClassicBuildPipelineCustomizer - { - public override Type[] UsedComponents { get; } = - { - typeof(EnableEntityNames) - }; - public override string[] ProvidePlayerScriptingDefines() - { - if (Context.HasComponent()) - { - return Array.Empty(); - } - - var classicBuildProfile = Context.GetComponentOrDefault(); - if (classicBuildProfile.Configuration == BuildType.Release) - { - string[] ret = {"DOTS_DISABLE_DEBUG_NAMES"}; - return ret; - } - - return Array.Empty(); - } - } -} -#endif diff --git a/Unity.Entities.Editor/Extensions/EntityManagerEditorExtensions.cs b/Unity.Entities.Editor/Extensions/EntityManagerEditorExtensions.cs index df126a4..7118e45 100644 --- a/Unity.Entities.Editor/Extensions/EntityManagerEditorExtensions.cs +++ b/Unity.Entities.Editor/Extensions/EntityManagerEditorExtensions.cs @@ -4,8 +4,11 @@ static class EntityManagerEditorExtensions { public static bool SafeExists(this EntityManager entityManager, Entity entity) { - return entity.Index >= 0 && (uint)entity.Index < (uint)entityManager.EntityCapacity - && entityManager.Exists(entity); + // If we are in the middle of an exclusive transaction, we can't safely check if an entity exists. + if (!entityManager.CanBeginExclusiveEntityTransaction()) + entityManager.EndExclusiveEntityTransaction(); + + return entity.Index >= 0 && (uint)entity.Index < (uint)entityManager.EntityCapacity && entityManager.Exists(entity); } } } diff --git a/Unity.Entities.Editor/GUI/Legacy/Controls/RuntimeComponents/RuntimeComponentsDrawer.cs b/Unity.Entities.Editor/GUI/Legacy/Controls/RuntimeComponents/RuntimeComponentsDrawer.cs index 541b918..5d986f5 100644 --- a/Unity.Entities.Editor/GUI/Legacy/Controls/RuntimeComponents/RuntimeComponentsDrawer.cs +++ b/Unity.Entities.Editor/GUI/Legacy/Controls/RuntimeComponents/RuntimeComponentsDrawer.cs @@ -95,7 +95,7 @@ public void SetComponentTypes(IEnumerable components) /// public void OnGUI() { - m_Targets.RemoveAll(data => !data.EntityManager.SafeExists(data.Entity)); + m_Targets.RemoveAll(data => !data.World.EntityManager.SafeExists(data.Entity)); if (m_Targets.Count == 0) return; diff --git a/Unity.Entities.Editor/Hierarchy/HierarchySettings.cs b/Unity.Entities.Editor/Hierarchy/HierarchySettings.cs index e38df59..a1cdbc9 100644 --- a/Unity.Entities.Editor/Hierarchy/HierarchySettings.cs +++ b/Unity.Entities.Editor/Hierarchy/HierarchySettings.cs @@ -20,6 +20,11 @@ void ISetting.OnSettingChanged(PropertyPath path) UseAdvanceSearchSettingChanged?.Invoke(); } + string[] ISetting.GetSearchKeywords() + { + return ISetting.GetSearchKeywordsFromType(Configuration.GetType()); + } + [UsedImplicitly] class Inspector : PropertyInspector { diff --git a/Unity.Entities.Editor/Hierarchy/HierarchyWindow.cs b/Unity.Entities.Editor/Hierarchy/HierarchyWindow.cs index 917db5d..3e69a68 100644 --- a/Unity.Entities.Editor/Hierarchy/HierarchyWindow.cs +++ b/Unity.Entities.Editor/Hierarchy/HierarchyWindow.cs @@ -32,7 +32,7 @@ partial class HierarchyWindow : DOTSEditorWindow static readonly string k_WindowName = L10n.Tr("Entities Hierarchy"); static readonly Vector2 k_MinWindowSize = Constants.MinWindowSize; - static readonly EntityQueryOptions[] k_EntityQueryOptions = new [] { + static readonly EntityQueryOptions[] k_EntityQueryOptions = new[] { EntityQueryOptions.FilterWriteGroup, EntityQueryOptions.IgnoreComponentEnabledState, EntityQueryOptions.IncludeDisabledEntities, @@ -86,6 +86,8 @@ protected override void OnCreate() Resources.Templates.DotsEditorCommon.AddStyles(rootVisualElement); Resources.AddCommonVariables(rootVisualElement); + dataModeController.UpdateSupportedDataModes(GetSupportedDataModes(), GetPreferredDataMode()); + // Initialize the data models. m_HierarchySettings = UserSettings.GetOrCreate(Constants.Settings.Hierarchy); m_Hierarchy = new Hierarchy(Allocator.Persistent, dataModeController.dataMode) @@ -102,9 +104,6 @@ protected override void OnCreate() m_HierarchyElement.SetDecorators(s_Decorators); Selection.selectionChanged += OnGlobalSelectionChanged; - - // Data mode. - dataModeController.UpdateSupportedDataModes(GetSupportedDataModes(), GetPreferredDataMode()); dataModeController.dataModeChanged += OnDataModeChanged; EditorApplication.playModeStateChanged += OnPlayModeStateChanged; } @@ -222,12 +221,12 @@ void SetupSearchOptions() m_SearchElement.AddSearchFilterPopupItem(Constants.ComponentSearch.Any, k_FilterAnyComponentType, k_FilterAnyComponentType, Constants.ComponentSearch.Op); m_SearchElement.AddSearchFilterPopupItem(Constants.Hierarchy.EntityIndexToken, k_FilterIndexToken, k_FilterIndexTokenTooltip, "="); - foreach(var opt in k_EntityQueryOptions) + foreach (var opt in k_EntityQueryOptions) { m_SearchElement.AddSearchFilterPopupItem($"+{opt}", "Option", k_FilterIndexTokenTooltip, " ", isCompleteFilter: true); } - foreach(var k in k_NodeKinds) + foreach (var k in k_NodeKinds) { m_SearchElement.AddSearchFilterPopupItem($"{Constants.Hierarchy.KindToken}={k}", k_FilterKindToken, k_FilterKindTokenTooltip, " ", isCompleteFilter: true); } @@ -308,75 +307,80 @@ public static void SelectHierarchyNode(Hierarchy hierarchy, HierarchyNodeHandle switch (handle.Kind) { case NodeKind.Entity: + { + var entity = handle.ToEntity(); + + if (entity != Entity.Null) { - var entity = handle.ToEntity(); + var world = hierarchy.World; + + // If we are in the middle of a transaction, we need to end it before we can get the authoring object + if (!world.EntityManager.CanBeginExclusiveEntityTransaction()) + world.EntityManager.EndExclusiveEntityTransaction(); + + var authoringObject = world.EntityManager.Debug.GetAuthoringObjectForEntity(entity); - if (entity != Entity.Null) + if (authoringObject == null) { - var world = hierarchy.World; - var authoringObject = world.EntityManager.Debug.GetAuthoringObjectForEntity(entity); - - if (authoringObject == null) - { - EntitySelectionProxy.SelectEntity(world, entity); - } - else - { - var context = EntitySelectionProxy.CreateInstance(world, entity); - // Selected entities should always try to show up in Runtime mode - SelectionBridge.SetSelection(authoringObject, context, DataMode.Runtime); - Undo.SetCurrentGroupName($"Select {authoringObject.name} ({authoringObject.GetType().Name})"); - } + EntitySelectionProxy.SelectEntity(world, entity); + } + else + { + var context = EntitySelectionProxy.CreateInstance(world, entity); + // Selected entities should always try to show up in Runtime mode + SelectionBridge.SetSelection(authoringObject, context, DataMode.Runtime); + Undo.SetCurrentGroupName($"Select {authoringObject.name} ({authoringObject.GetType().Name})"); } - - break; } + break; + } + case NodeKind.SubScene: - { - var subScene = hierarchy.SubSceneMap.GetSubSceneMonobehaviourFromHandle(handle); - SelectionBridge.SetSelection(subScene ? subScene.gameObject : null, HierarchySelectionContext.CreateInstance(handle), DataMode.Disabled); - if (subScene) - Undo.SetCurrentGroupName($"Select {subScene.name} ({subScene.GetType().Name})"); + { + var subScene = hierarchy.SubSceneMap.GetSubSceneMonobehaviourFromHandle(handle); + SelectionBridge.SetSelection(subScene ? subScene.gameObject : null, HierarchySelectionContext.CreateInstance(handle), DataMode.Disabled); + if (subScene) + Undo.SetCurrentGroupName($"Select {subScene.name} ({subScene.GetType().Name})"); - break; - } + break; + } case NodeKind.Scene: - { - SelectionBridge.SetSelection(null, HierarchySelectionContext.CreateInstance(handle), DataMode.Disabled); - break; - } + { + SelectionBridge.SetSelection(null, HierarchySelectionContext.CreateInstance(handle), DataMode.Disabled); + break; + } case NodeKind.GameObject: - { - var gameObject = hierarchy.GetUnityObject(handle) as GameObject; + { + var gameObject = hierarchy.GetUnityObject(handle) as GameObject; - // Don't reselect yourself - if (Selection.activeObject == gameObject) - return; + // Don't reselect yourself + if (Selection.activeObject == gameObject) + return; - var world = hierarchy.World; - EntitySelectionProxy context; + var world = hierarchy.World; + EntitySelectionProxy context; - if (world is not { IsCreated: true }) - { - context = null; - } - else - { - var primaryEntity = world.EntityManager.Debug.GetPrimaryEntityForAuthoringObject(gameObject); - - context = primaryEntity != Entity.Null && world.EntityManager.SafeExists(primaryEntity) - ? EntitySelectionProxy.CreateInstance(world, primaryEntity) - : null; - } + if (world is not { IsCreated: true }) + { + context = null; + } + else + { + var primaryEntity = world.EntityManager.Debug.GetPrimaryEntityForAuthoringObject(gameObject); - // Selected GameObjects should use whatever the current DataMode for the hierarchy is. - SelectionBridge.SetSelection(gameObject, context, dataMode); - Undo.SetCurrentGroupName($"Select {gameObject.name} ({gameObject.GetType().Name})"); - break; + context = primaryEntity != Entity.Null && world.EntityManager.SafeExists(primaryEntity) + ? EntitySelectionProxy.CreateInstance(world, primaryEntity) + : null; } + + // Selected GameObjects should use whatever the current DataMode for the hierarchy is. + SelectionBridge.SetSelection(gameObject, context, dataMode); + Undo.SetCurrentGroupName($"Select {gameObject.name} ({gameObject.GetType().Name})"); + break; + } } Undo.CollapseUndoOperations(undoGroup); diff --git a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/ComponentDataDiffer.cs b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/ComponentDataDiffer.cs index b011019..9482a93 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/ComponentDataDiffer.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/ComponentDataDiffer.cs @@ -10,7 +10,7 @@ namespace Unity.Entities.Editor /// /// The is use to efficiently track changes for a given type over time. /// - class ComponentDataDiffer : IDisposable + unsafe class ComponentDataDiffer : IDisposable { /// /// The structure returned when gathering changes. This can be used to unpack all changes since the last diff. @@ -224,7 +224,7 @@ public void Dispose() /// unsafe struct ChunkChanges : IDisposable { - public Chunk* Chunk; + public ChunkIndex Chunk; public ChunkShadow Shadow; public UnsafeList AddedComponentData; public UnsafeList RemovedComponentData; @@ -233,7 +233,7 @@ unsafe struct ChunkChanges : IDisposable public void Dispose() { - Chunk = null; + Chunk = ChunkIndex.Null; Shadow = default; AddedComponentData.Dispose(); RemovedComponentData.Dispose(); @@ -242,6 +242,8 @@ public void Dispose() } } + readonly EntityComponentStore* m_EntityComponentStore; + /// /// The type index for the tracked component. /// @@ -316,7 +318,7 @@ public static bool CanWatch(ComponentType componentType) /// /// The component type to diff. /// The specified component can not be watched by the differ. - public ComponentDataDiffer(ComponentType componentType) + public ComponentDataDiffer(EntityComponentStore* ecs, ComponentType componentType) { if (!CanWatch(componentType)) throw new ArgumentException($"{nameof(ComponentDataDiffer)} only supports unmanaged {nameof(IComponentData)} components.", nameof(componentType)); @@ -335,6 +337,7 @@ public ComponentDataDiffer(ComponentType componentType) m_RemovedChunks = new NativeList(Allocator.Persistent); m_RemovedEntities = new NativeList(Allocator.Persistent); m_RemovedComponents = new NativeList(Allocator.Persistent); + m_EntityComponentStore = ecs; } public void Dispose() @@ -351,6 +354,7 @@ public void Dispose() m_ChangesByChunk.Dispose(); m_RemovedChunks.Dispose(); m_RemovedEntities.Dispose(); + m_RemovedComponents.Dispose(); } /// @@ -450,6 +454,7 @@ public unsafe ChangeSet GatherComponentChangesAsync(EntityQuery query, Allocator { TypeIndex = m_TypeIndex, Chunks = chunks, + EntityComponentStore = m_EntityComponentStore, ComponentSize = m_ComponentSize, GatheredChangesByChunk = m_ChangesByChunk.AsDeferredJobArray(), AllocatedChunkShadowsByChunk = m_AllocatedChunkShadowByChunk.AsDeferredJobArray(), @@ -506,15 +511,17 @@ unsafe struct GatherEntityAndComponentChangesJob : IJobParallelForDefer public void Execute(int index) { var chunk = Chunks[index].m_Chunk; - var archetype = chunk->Archetype; + var archetype = Chunks[index].Archetype.Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); if (indexInTypeArray == -1) return; - if (ShadowChunksBySequenceNumber.TryGetValue(chunk->SequenceNumber, out var shadow)) + if (ShadowChunksBySequenceNumber.TryGetValue(chunk.SequenceNumber, out var shadow)) { - if (!ChangeVersionUtility.DidChange(chunk->GetChangeVersion(indexInTypeArray), shadow.Ptr->ComponentVersion) && - !ChangeVersionUtility.DidChange(chunk->GetChangeVersion(0), shadow.Ptr->EntityVersion)) + var componentVersion = archetype->Chunks.GetChangeVersion(indexInTypeArray, chunk.ListIndex); + var entityVersion = archetype->Chunks.GetChangeVersion(0, chunk.ListIndex); + if (!ChangeVersionUtility.DidChange(componentVersion, shadow.Ptr->ComponentVersion) && + !ChangeVersionUtility.DidChange(entityVersion, shadow.Ptr->EntityVersion)) return; var changesForChunk = GatheredChanges + index; @@ -529,10 +536,10 @@ public void Execute(int index) changesForChunk->RemovedComponentData = new UnsafeList(0, Allocator.TempJob); } - var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; - var componentDataPtr = chunk->Buffer + archetype->Offsets[indexInTypeArray]; + var entityDataPtr = chunk.Buffer + archetype->Offsets[0]; + var componentDataPtr = chunk.Buffer + archetype->Offsets[indexInTypeArray]; - var currentCount = chunk->Count; + var currentCount = chunk.Count; var previousCount = shadow.Ptr->Count; var i = 0; @@ -568,14 +575,14 @@ public void Execute(int index) } else { - var addedComponentDataBuffer = new UnsafeList(chunk->Count * ComponentSize, Allocator.TempJob); - var addedComponentEntities = new UnsafeList(chunk->Count, Allocator.TempJob); + var addedComponentDataBuffer = new UnsafeList(chunk.Count * ComponentSize, Allocator.TempJob); + var addedComponentEntities = new UnsafeList(chunk.Count, Allocator.TempJob); - var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; - var componentDataPtr = chunk->Buffer + archetype->Offsets[indexInTypeArray]; + var entityDataPtr = chunk.Buffer + archetype->Offsets[0]; + var componentDataPtr = chunk.Buffer + archetype->Offsets[indexInTypeArray]; - addedComponentDataBuffer.AddRange(componentDataPtr, chunk->Count * ComponentSize); - addedComponentEntities.AddRange(entityDataPtr, chunk->Count); + addedComponentDataBuffer.AddRange(componentDataPtr, chunk.Count * ComponentSize); + addedComponentEntities.AddRange(entityDataPtr, chunk.Count); var changesForChunk = GatheredChanges + index; changesForChunk->Chunk = chunk; @@ -609,11 +616,12 @@ unsafe struct GatherExistingChunks : IJobParallelForDefer public void Execute(int index) { var chunk = Chunks[index].m_Chunk; + var archetype = Chunks[index].Archetype.Archetype; - if (ChunkDataUtility.GetIndexInTypeArray(chunk->Archetype, TypeIndex) == -1) // Archetype doesn't match required component + if (ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex) == -1) // Archetype doesn't match required component return; - ChunkSequenceNumbers.Add(chunk->SequenceNumber); + ChunkSequenceNumbers.Add(chunk.SequenceNumber); } } @@ -664,28 +672,30 @@ unsafe struct AllocateChunkShadowsJob : IJobParallelForDefer public void Execute(int index) { var chunk = Chunks[index].m_Chunk; - var archetype = chunk->Archetype; + var archetype = Chunks[index].Archetype.Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); if (indexInTypeArray == -1) return; - var sequenceNumber = chunk->SequenceNumber; + var capacity = archetype->ChunkCapacity; + var sequenceNumber = chunk.SequenceNumber; if (ChunkShadowBySequenceNumber.TryGetValue(sequenceNumber, out var shadow)) return; - var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; - var componentDataPtr = chunk->Buffer + archetype->Offsets[indexInTypeArray]; + var entityDataPtr = chunk.Buffer + archetype->Offsets[0]; + var componentDataPtr = chunk.Buffer + archetype->Offsets[indexInTypeArray]; shadow = new ChunkShadow(Allocator.Persistent); - shadow.Ptr->Count = chunk->Count; - shadow.Ptr->ComponentVersion = chunk->GetChangeVersion(indexInTypeArray); - shadow.Ptr->EntityVersion = chunk->GetChangeVersion(0); - shadow.Ptr->Entities = (byte*)Memory.Unmanaged.Allocate(sizeof(Entity) * chunk->Capacity, 4, Allocator.Persistent); - shadow.Ptr->Components = (byte*)Memory.Unmanaged.Allocate(ComponentSize * chunk->Capacity, 4, Allocator.Persistent); + var chunkCount = chunk.Count; + shadow.Ptr->Count = chunkCount; + shadow.Ptr->ComponentVersion = archetype->Chunks.GetChangeVersion(indexInTypeArray, chunk.ListIndex); + shadow.Ptr->EntityVersion = archetype->Chunks.GetChangeVersion(0, chunk.ListIndex); + shadow.Ptr->Entities = (byte*)Memory.Unmanaged.Allocate(sizeof(Entity) * capacity, 4, Allocator.Persistent); + shadow.Ptr->Components = (byte*)Memory.Unmanaged.Allocate(ComponentSize * capacity, 4, Allocator.Persistent); shadow.Ptr->SequenceNumber = sequenceNumber; - UnsafeUtility.MemCpy(shadow.Ptr->Entities, entityDataPtr, chunk->Count * sizeof(Entity)); - UnsafeUtility.MemCpy(shadow.Ptr->Components, componentDataPtr, chunk->Count * ComponentSize); + UnsafeUtility.MemCpy(shadow.Ptr->Entities, entityDataPtr, chunkCount * sizeof(Entity)); + UnsafeUtility.MemCpy(shadow.Ptr->Components, componentDataPtr, chunkCount * ComponentSize); AllocatedChunkShadowByChunk->ElementAt(index) = shadow; } @@ -763,6 +773,7 @@ unsafe struct UpdateShadowChunksJob : IJobParallelForDefer public TypeIndex TypeIndex; public int ComponentSize; + [NativeDisableUnsafePtrRestriction][ReadOnly] public EntityComponentStore* EntityComponentStore; [ReadOnly] public NativeList Chunks; // not used, but required to be here for IJobParallelForDefer [ReadOnly] public NativeArray GatheredChangesByChunk; [ReadOnly] public NativeArray AllocatedChunkShadowsByChunk; @@ -774,27 +785,31 @@ public void Execute(int index) var chunk = changes.Chunk; - if (null == chunk) + if (ChunkIndex.Null == chunk) return; if (!changes.Shadow.IsCreated) { - ChunkShadowBySequenceNumber.TryAdd(chunk->SequenceNumber, AllocatedChunkShadowsByChunk[index]); + ChunkShadowBySequenceNumber.TryAdd(chunk.SequenceNumber, AllocatedChunkShadowsByChunk[index]); } else { - var archetype = chunk->Archetype; + var archetype = EntityComponentStore->GetArchetype(chunk); var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); - var entities = chunk->Buffer + archetype->Offsets[0]; - var components = chunk->Buffer + archetype->Offsets[indexInTypeArray]; + var buffer = chunk.Buffer; + var entities = buffer + archetype->Offsets[0]; + var components = buffer + archetype->Offsets[indexInTypeArray]; + var listIndex = chunk.ListIndex; + + changes.Shadow.Ptr->Count = changes.Chunk.Count; + changes.Shadow.Ptr->EntityVersion = archetype->Chunks.GetChangeVersion(0, listIndex); + changes.Shadow.Ptr->ComponentVersion = archetype->Chunks.GetChangeVersion(indexInTypeArray, listIndex); - changes.Shadow.Ptr->Count = changes.Chunk->Count; - changes.Shadow.Ptr->EntityVersion = chunk->GetChangeVersion(0); - changes.Shadow.Ptr->ComponentVersion = chunk->GetChangeVersion(indexInTypeArray); + var entityCount = chunk.Count; - UnsafeUtility.MemCpy(changes.Shadow.Ptr->Entities, entities, chunk->Count * sizeof(Entity)); - UnsafeUtility.MemCpy(changes.Shadow.Ptr->Components, components, chunk->Count * ComponentSize); + UnsafeUtility.MemCpy(changes.Shadow.Ptr->Entities, entities, entityCount * sizeof(Entity)); + UnsafeUtility.MemCpy(changes.Shadow.Ptr->Components, components, entityCount * ComponentSize); } } } diff --git a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/EntityDiffer.cs b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/EntityDiffer.cs index 8cd54ad..176ed90 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/EntityDiffer.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/EntityDiffer.cs @@ -71,14 +71,14 @@ public void Dispose() /// unsafe struct ChunkChanges : IDisposable { - public Chunk* Chunk; + public ChunkIndex Chunk; public ChunkShadow Shadow; public UnsafeList AddedEntities; public UnsafeList RemovedEntities; public void Dispose() { - Chunk = null; + Chunk = ChunkIndex.Null; Shadow = default; AddedEntities.Dispose(); RemovedEntities.Dispose(); @@ -195,6 +195,7 @@ public unsafe JobHandle GetEntityQueryMatchDiffAsync(EntityQuery query, NativeLi var updateShadowChunksJobHandle = new UpdateShadowChunksJob { + EntityComponentStore = query.GetEntityComponentStore(), Chunks = chunks, ChangesByChunk = m_ChangesByChunk.AsDeferredJobArray(), AllocatedChunkShadowsByChunk = m_AllocatedChunkShadowByChunk.AsDeferredJobArray(), @@ -245,11 +246,12 @@ unsafe struct GatherEntityChangesJob : IJobParallelForDefer public void Execute(int index) { var chunk = Chunks[index].m_Chunk; - var archetype = chunk->Archetype; + var archetype = Chunks[index].Archetype.Archetype; - if (ChunkShadowBySequenceNumber.TryGetValue(chunk->SequenceNumber, out var shadow)) + if (ChunkShadowBySequenceNumber.TryGetValue(chunk.SequenceNumber, out var shadow)) { - if (!ChangeVersionUtility.DidChange(chunk->GetChangeVersion(0), shadow.Ptr->Version)) + var entityVersion = archetype->Chunks.GetChangeVersion(0, chunk.ListIndex); + if (!ChangeVersionUtility.DidChange(entityVersion, shadow.Ptr->Version)) return; var changesForChunk = ChangesByChunk + index; @@ -258,10 +260,10 @@ public void Execute(int index) changesForChunk->AddedEntities = new UnsafeList(0, Allocator.TempJob); changesForChunk->RemovedEntities = new UnsafeList(0, Allocator.TempJob); - var currentEntity = (Entity*)(chunk->Buffer + archetype->Offsets[0]); + var currentEntity = (Entity*)(chunk.Buffer + archetype->Offsets[0]); var entityFromShadow = (Entity*)shadow.Ptr->Entities; - var currentCount = chunk->Count; + var currentCount = chunk.Count; var shadowCount = shadow.Ptr->Count; var i = 0; @@ -287,9 +289,9 @@ public void Execute(int index) } else { - var addedComponentEntities = new UnsafeList(chunk->Count, Allocator.TempJob); - var entities = chunk->Buffer + archetype->Offsets[0]; - addedComponentEntities.AddRange(entities, chunk->Count); + var addedComponentEntities = new UnsafeList(chunk.Count, Allocator.TempJob); + var entities = chunk.Buffer + archetype->Offsets[0]; + addedComponentEntities.AddRange(entities, chunk.Count); var changesForChunk = ChangesByChunk + index; changesForChunk->Chunk = chunk; @@ -308,7 +310,7 @@ unsafe struct GatherExistingChunksJob : IJobParallelForDefer public void Execute(int index) { - ChunkSequenceNumbers.Add(Chunks[index].m_Chunk->SequenceNumber); + ChunkSequenceNumbers.Add(Chunks[index].m_Chunk.SequenceNumber); } } @@ -352,21 +354,22 @@ unsafe struct AllocateChunkShadowsJob : IJobParallelForDefer public void Execute(int index) { var chunk = Chunks[index].m_Chunk; - var archetype = chunk->Archetype; - var sequenceNumber = chunk->SequenceNumber; + var archetype = Chunks[index].Archetype.Archetype; + var chunkCount = chunk.Count; + var sequenceNumber = chunk.SequenceNumber; if (ShadowChunksBySequenceNumber.TryGetValue(sequenceNumber, out var shadow)) return; - var entities = chunk->Buffer + archetype->Offsets[0]; + var entities = chunk.Buffer + archetype->Offsets[0]; shadow = new ChunkShadow(Allocator.Persistent); - shadow.Ptr->Count = chunk->Count; - shadow.Ptr->Version = chunk->GetChangeVersion(0); - shadow.Ptr->Entities = (Entity*)Memory.Unmanaged.Allocate(sizeof(Entity) * chunk->Capacity, 4, Allocator.Persistent); + shadow.Ptr->Count = chunkCount; + shadow.Ptr->Version = archetype->Chunks.GetChangeVersion(0, chunk.ListIndex); + shadow.Ptr->Entities = (Entity*)Memory.Unmanaged.Allocate(sizeof(Entity) * archetype->ChunkCapacity, 4, Allocator.Persistent); shadow.Ptr->SequenceNumber = sequenceNumber; - UnsafeUtility.MemCpy(shadow.Ptr->Entities, entities, chunk->Count * sizeof(Entity)); + UnsafeUtility.MemCpy(shadow.Ptr->Entities, entities, chunkCount * sizeof(Entity)); *(AllocatedShadowChunks + index) = shadow; } @@ -424,6 +427,7 @@ public void Execute() [BurstCompile] unsafe struct UpdateShadowChunksJob : IJobParallelForDefer { + [NativeDisableUnsafePtrRestriction][ReadOnly] public EntityComponentStore* EntityComponentStore; [ReadOnly] public NativeList Chunks; // not used, but required to be here for IJobParallelForDefer [ReadOnly] public NativeArray ChangesByChunk; [ReadOnly] public NativeArray AllocatedChunkShadowsByChunk; @@ -435,22 +439,23 @@ public void Execute(int index) var chunk = changes.Chunk; - if (null == chunk) + if (ChunkIndex.Null == chunk) return; if (AllocatedChunkShadowsByChunk[index].IsCreated) { - ChunkShadowBySequenceNumber.TryAdd(chunk->SequenceNumber, AllocatedChunkShadowsByChunk[index]); + ChunkShadowBySequenceNumber.TryAdd(chunk.SequenceNumber, AllocatedChunkShadowsByChunk[index]); } else { - var archetype = chunk->Archetype; - var entities = chunk->Buffer + archetype->Offsets[0]; + var archetype = EntityComponentStore->GetArchetype(chunk); + var entities = chunk.Buffer + archetype->Offsets[0]; + var entitiesCount = chunk.Count; - changes.Shadow.Ptr->Count = changes.Chunk->Count; - changes.Shadow.Ptr->Version = chunk->GetChangeVersion(0); + changes.Shadow.Ptr->Count = entitiesCount; + changes.Shadow.Ptr->Version = archetype->Chunks.GetChangeVersion(0, chunk.ListIndex); - UnsafeUtility.MemCpy(changes.Shadow.Ptr->Entities, entities, chunk->Count * sizeof(Entity)); + UnsafeUtility.MemCpy(changes.Shadow.Ptr->Entities, entities, entitiesCount * sizeof(Entity)); } } } diff --git a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/HierarchyEntityChangeTracker.cs b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/HierarchyEntityChangeTracker.cs index 2777d0f..df8aad0 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/HierarchyEntityChangeTracker.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/HierarchyEntityChangeTracker.cs @@ -205,11 +205,12 @@ void RebuildQueryCache(EntityQueryDesc value) // Component that is never added to an Entity, used to create EntityQuery that is always empty. private struct UnusedTag : IComponentData {} - public HierarchyEntityChangeTracker(World world, Allocator allocator) + public unsafe HierarchyEntityChangeTracker(World world, Allocator allocator) { m_World = world; + var ecs = world.EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore; m_EntityChangeTracker = new EntityDiffer(world); - m_ParentChangeTracker = new ComponentDataDiffer(ComponentType.ReadOnly()); + m_ParentChangeTracker = new ComponentDataDiffer(ecs, ComponentType.ReadOnly()); m_SceneTagWithoutParentChangeTracker = new UnmanagedSharedComponentDataDiffer(ComponentType.ReadOnly()); m_DistinctBuffer = new NativeList(16, allocator); diff --git a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SharedComponentDataDiffer.cs b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SharedComponentDataDiffer.cs index 0c59b19..65e66b2 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SharedComponentDataDiffer.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SharedComponentDataDiffer.cs @@ -157,6 +157,7 @@ public unsafe ComponentChanges GatherComponentChanges(EntityManager entityManage var concatResultsJob = new ConcatResultsJob { + EntityComponentStore = query.GetEntityComponentStore(), TypeIndex = m_TypeIndex, GatheredChanges = m_GatheredChanges, RemovedShadowChunks = m_RemovedShadowChunks.AsDeferredJobArray(), @@ -284,16 +285,17 @@ unsafe struct GatherChangesJob : IJobParallelForDefer public void Execute(int index) { var chunk = Chunks[index].m_Chunk; - var archetype = chunk->Archetype; + var archetype = Chunks[index].Archetype.Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); if (indexInTypeArray == -1) // Archetype doesn't match required component return; var changesForChunk = GatheredChanges + index; - if (ShadowChunksBySequenceNumber.TryGetValue(chunk->SequenceNumber, out var shadow)) + if (ShadowChunksBySequenceNumber.TryGetValue(chunk.SequenceNumber, out var shadow)) { - if (!ChangeVersionUtility.DidChange(chunk->GetChangeVersion(0), shadow.Version)) + var entityVersion = archetype->Chunks.GetChangeVersion(0, chunk.ListIndex); + if (!ChangeVersionUtility.DidChange(entityVersion, shadow.Version)) return; if (!changesForChunk->AddedEntities.IsCreated) @@ -303,8 +305,8 @@ public void Execute(int index) changesForChunk->RemovedEntities = new UnsafeList(0, Allocator.TempJob); } - var entityDataPtr = (Entity*)(chunk->Buffer + archetype->Offsets[0]); - var currentCount = chunk->Count; + var entityDataPtr = (Entity*)(chunk.Buffer + archetype->Offsets[0]); + var currentCount = chunk.Count; var previousCount = shadow.EntityCount; var i = 0; for (; i < currentCount && i < previousCount; i++) @@ -335,9 +337,9 @@ public void Execute(int index) else { // This is a new chunk - var addedEntities = new UnsafeList(chunk->Count, Allocator.TempJob); - var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; - addedEntities.AddRange(entityDataPtr, chunk->Count); + var addedEntities = new UnsafeList(chunk.Count, Allocator.TempJob); + var entityDataPtr = chunk.Buffer + archetype->Offsets[0]; + addedEntities.AddRange(entityDataPtr, chunk.Count); changesForChunk->Chunk = chunk; changesForChunk->AddedEntities = addedEntities; } @@ -355,25 +357,26 @@ unsafe struct AllocateNewShadowChunksJob : IJobParallelForDefer public void Execute(int index) { var chunk = Chunks[index].m_Chunk; - var archetype = chunk->Archetype; + var archetype = Chunks[index].Archetype.Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); if (indexInTypeArray == -1) // Archetype doesn't match required component return; - var sequenceNumber = chunk->SequenceNumber; + var sequenceNumber = chunk.SequenceNumber; if (ShadowChunksBySequenceNumber.TryGetValue(sequenceNumber, out var shadow)) return; - var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; + var entityDataPtr = chunk.Buffer + archetype->Offsets[0]; + var entityCount = chunk.Count; shadow = new ShadowChunk { - EntityCount = chunk->Count, - Version = chunk->GetChangeVersion(0), - EntityDataBuffer = (Entity*)UnsafeUtility.Malloc(sizeof(Entity) * chunk->Capacity, 4, Allocator.Persistent), + EntityCount = entityCount, + Version = archetype->Chunks.GetChangeVersion(0, chunk.ListIndex), + EntityDataBuffer = (Entity*)UnsafeUtility.Malloc(sizeof(Entity) * archetype->ChunkCapacity, 4, Allocator.Persistent), }; - UnsafeUtility.MemCpy(shadow.EntityDataBuffer, entityDataPtr, chunk->Count * sizeof(Entity)); + UnsafeUtility.MemCpy(shadow.EntityDataBuffer, entityDataPtr, entityCount * sizeof(Entity)); AllocatedShadowChunks[index] = shadow; } @@ -396,24 +399,24 @@ public void Execute() for (var index = 0; index < Chunks.Length; index++) { var chunk = Chunks[index].m_Chunk; - var archetype = chunk->Archetype; + var archetype = Chunks[index].Archetype.Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); if (indexInTypeArray == -1) // Archetype doesn't match required component continue; - var version = chunk->GetChangeVersion(0); - var sequenceNumber = chunk->SequenceNumber; + var version = archetype->Chunks.GetChangeVersion(0, chunk.ListIndex); + var sequenceNumber = chunk.SequenceNumber; processedChunks[sequenceNumber] = 0; - var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; + var entityDataPtr = chunk.Buffer + archetype->Offsets[0]; if (ShadowChunksBySequenceNumber.TryGetValue(sequenceNumber, out var shadow)) { if (!ChangeVersionUtility.DidChange(version, shadow.Version)) continue; - UnsafeUtility.MemCpy(shadow.EntityDataBuffer, entityDataPtr, chunk->Count * sizeof(Entity)); + UnsafeUtility.MemCpy(shadow.EntityDataBuffer, entityDataPtr, chunk.Count * sizeof(Entity)); - shadow.EntityCount = chunk->Count; + shadow.EntityCount = chunk.Count; shadow.Version = version; ShadowChunksBySequenceNumber[sequenceNumber] = shadow; @@ -491,6 +494,8 @@ unsafe struct ConcatResultsJob : IJob { public TypeIndex TypeIndex; + [NativeDisableUnsafePtrRestriction] [ReadOnly] public EntityComponentStore* EntityComponentStore; + [ReadOnly] public NativeList GatheredChanges; [ReadOnly] public NativeArray RemovedShadowChunks; @@ -536,7 +541,7 @@ public void Execute() UnsafeUtility.MemCpyReplicate((int*)RemovedEntitiesMappingToComponent.GetUnsafePtr() + removedEntityCurrentCount, &removedSharedComponentsCount, sizeof(int), changes.RemovedEntities.Length); removedEntityCurrentCount += changes.RemovedEntities.Length; - IndicesInManagedComponentStore[removedSharedComponentsCount++] = SharedComponentValueIndexByChunk[changes.Chunk->SequenceNumber]; + IndicesInManagedComponentStore[removedSharedComponentsCount++] = SharedComponentValueIndexByChunk[changes.Chunk.SequenceNumber]; } for (var i = 0; i < GatheredChanges.Length; i++) @@ -545,13 +550,13 @@ public void Execute() if (changes.AddedEntities.Length == 0) continue; - var chunkSequenceNumber = changes.Chunk->SequenceNumber; + var chunkSequenceNumber = changes.Chunk.SequenceNumber; if (changes.AddedEntities.Length > 0) { - var archetype = changes.Chunk->Archetype; + var archetype = EntityComponentStore->GetArchetype(changes.Chunk); var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); - var sharedComponentValueArray = changes.Chunk->SharedComponentValues; + var sharedComponentValueArray = archetype->Chunks.GetSharedComponentValues(changes.Chunk.ListIndex); var sharedComponentOffset = indexInTypeArray - archetype->FirstSharedComponent; var sharedComponentDataIndex = sharedComponentValueArray[sharedComponentOffset]; @@ -571,13 +576,13 @@ public void Execute() unsafe struct ChangesCollector : IDisposable { - public Chunk* Chunk; + public ChunkIndex Chunk; public UnsafeList AddedEntities; public UnsafeList RemovedEntities; public void Dispose() { - Chunk = null; + Chunk = ChunkIndex.Null; if (AddedEntities.IsCreated) AddedEntities.Dispose(); if (RemovedEntities.IsCreated) diff --git a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SubSceneChangeTracker.cs b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SubSceneChangeTracker.cs index f916dd2..1781d94 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SubSceneChangeTracker.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/SubSceneChangeTracker.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using Unity.Collections; using Unity.Editor.Bridge; @@ -33,10 +33,16 @@ public void SetWorld(World world) m_SceneTagDiffer.Clear(); m_World = world; - if (m_World is not null) + if (m_World != null && m_World.IsCreated) { + var entityManager = m_World.EntityManager; + + // Cannot create entity query during exclusive transaction, so make sure to complete it + if (!entityManager.CanBeginExclusiveEntityTransaction()) + entityManager.EndExclusiveEntityTransaction(); + using var qb = new EntityQueryBuilder(Allocator.TempJob); - m_Query = qb.WithAll().Build(world.EntityManager); + m_Query = qb.WithAll().Build(entityManager); } } diff --git a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/UnmanagedSharedComponentDataDiffer.cs b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/UnmanagedSharedComponentDataDiffer.cs index 92c6596..3f1ec1b 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/UnmanagedSharedComponentDataDiffer.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/ChangeTracking/UnmanagedSharedComponentDataDiffer.cs @@ -234,14 +234,14 @@ public void Dispose() /// unsafe struct ChunkChanges { - public Chunk* Chunk; + public ChunkIndex Chunk; public ChunkShadow Shadow; public UnsafeList AddedEntities; public UnsafeList RemovedEntities; public void Dispose() { - Chunk = null; + Chunk = ChunkIndex.Null; Shadow = default; AddedEntities.Dispose(); RemovedEntities.Dispose(); @@ -371,7 +371,7 @@ public ChangeSet GatherComponentChanges(EntityManager entityManager, EntityQuery public unsafe ChangeSet GatherComponentChangesAsync(EntityManager entityManager, EntityQuery query, Allocator allocator, out JobHandle jobHandle) { - var access = entityManager.GetCheckedEntityDataAccess(); + var access = entityManager.GetCheckedEntityDataAccessExclusive(); var chunks = query.ToArchetypeChunkListAsync(Allocator.TempJob, out var chunksJobHandle); m_ChunkSequenceNumbers.Clear(); @@ -440,6 +440,7 @@ public unsafe ChangeSet GatherComponentChangesAsync(EntityManager entityManager, var buildChangeSetJobHandle = new BuildChangeSetJob { + ECS = access->EntityComponentStore, TypeIndex = m_TypeIndex, ComponentSize = m_ComponentSize, Allocator = allocator, @@ -453,6 +454,7 @@ public unsafe ChangeSet GatherComponentChangesAsync(EntityManager entityManager, var updateShadowChunksJobHandle = new UpdateShadowChunksJob { + ECS = access->EntityComponentStore, Chunks = chunks, TypeIndex = m_TypeIndex, ChangesByChunk = m_ChangesByChunk.AsDeferredJobArray(), @@ -500,11 +502,12 @@ unsafe struct GatherExistingChunks : IJobParallelForDefer public void Execute(int index) { var chunk = Chunks[index].m_Chunk; + var archetype = Chunks[index].Archetype.Archetype; - if (ChunkDataUtility.GetIndexInTypeArray(chunk->Archetype, TypeIndex) == -1) // Archetype doesn't match required component + if (ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex) == -1) // Archetype doesn't match required component return; - ChunkSequenceNumbers.Add(chunk->SequenceNumber); + ChunkSequenceNumbers.Add(chunk.SequenceNumber); } } @@ -568,13 +571,14 @@ unsafe struct GatherEntityChangesJob : IJobParallelForDefer public void Execute(int index) { var chunk = Chunks[index].m_Chunk; - var archetype = chunk->Archetype; + var archetype = Chunks[index].Archetype.Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); if (indexInTypeArray == -1) return; - if (ShadowChunksBySequenceNumber.TryGetValue(chunk->SequenceNumber, out var shadow)) + if (ShadowChunksBySequenceNumber.TryGetValue(chunk.SequenceNumber, out var shadow)) { - if (!ChangeVersionUtility.DidChange(chunk->GetChangeVersion(0), shadow.Ptr->Version)) + var entityVersion = archetype->Chunks.GetChangeVersion(0, chunk.ListIndex); + if (!ChangeVersionUtility.DidChange(entityVersion, shadow.Ptr->Version)) return; var changesForChunk = GatheredChangesByChunk + index; @@ -583,8 +587,8 @@ public void Execute(int index) changesForChunk->AddedEntities = new UnsafeList(0, Allocator.TempJob); changesForChunk->RemovedEntities = new UnsafeList(0, Allocator.TempJob); - var entityDataPtr = (Entity*)(chunk->Buffer + archetype->Offsets[0]); - var currentCount = chunk->Count; + var entityDataPtr = (Entity*)(chunk.Buffer + archetype->Offsets[0]); + var currentCount = chunk.Count; var previousCount = shadow.Ptr->Count; var i = 0; @@ -609,9 +613,9 @@ public void Execute(int index) else { // This is a new chunk - var addedEntities = new UnsafeList(chunk->Count, Allocator.TempJob); - var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; - addedEntities.AddRange(entityDataPtr, chunk->Count); + var addedEntities = new UnsafeList(chunk.Count, Allocator.TempJob); + var entityDataPtr = chunk.Buffer + archetype->Offsets[0]; + addedEntities.AddRange(entityDataPtr, chunk.Count); var changesForChunk = GatheredChangesByChunk + index; changesForChunk->Chunk = chunk; @@ -636,29 +640,30 @@ unsafe struct AllocateChunkShadowsJob : IJobParallelForDefer public void Execute(int index) { var chunk = Chunks[index].m_Chunk; - var archetype = chunk->Archetype; + var archetype = Chunks[index].Archetype.Archetype; var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); if (indexInTypeArray == -1) return; - var sequenceNumber = chunk->SequenceNumber; + var sequenceNumber = chunk.SequenceNumber; if (ChunkShadowBySequenceNumber.TryGetValue(sequenceNumber, out var shadow)) return; - var sharedComponentValueArray = chunk->SharedComponentValues; + var sharedComponentValueArray = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); var sharedComponentOffset = indexInTypeArray - archetype->FirstSharedComponent; var sharedComponentDataIndex = sharedComponentValueArray[sharedComponentOffset]; var elementIndex = EntityComponentStore.GetElementIndexFromSharedComponentIndex(sharedComponentDataIndex); - var entityDataPtr = chunk->Buffer + archetype->Offsets[0]; + var entityDataPtr = chunk.Buffer + archetype->Offsets[0]; + var chunkCount = chunk.Count; shadow = new ChunkShadow(Allocator.Persistent); - shadow.Ptr->SequenceNumber = chunk->SequenceNumber; - shadow.Ptr->Count = chunk->Count; - shadow.Ptr->Version = chunk->GetChangeVersion(0); + shadow.Ptr->SequenceNumber = sequenceNumber; + shadow.Ptr->Count = chunkCount; + shadow.Ptr->Version = archetype->Chunks.GetChangeVersion(0, chunk.ListIndex); shadow.Ptr->SharedComponentElementIndex = elementIndex; - shadow.Ptr->Entities = (Entity*)Memory.Unmanaged.Allocate(sizeof(Entity) * chunk->Capacity, 4, Allocator.Persistent); + shadow.Ptr->Entities = (Entity*)Memory.Unmanaged.Allocate(sizeof(Entity) * archetype->ChunkCapacity, 4, Allocator.Persistent); - UnsafeUtility.MemCpy(shadow.Ptr->Entities, entityDataPtr, chunk->Count * sizeof(Entity)); + UnsafeUtility.MemCpy(shadow.Ptr->Entities, entityDataPtr, chunkCount * sizeof(Entity)); AllocatedChunkShadowsByChunk->ElementAt(index) = shadow; } } @@ -670,6 +675,8 @@ unsafe struct BuildChangeSetJob : IJob public int ComponentSize; public Allocator Allocator; + [NativeDisableUnsafePtrRestriction] public EntityComponentStore* ECS; + /// /// The set of changes gathered for this cycle. /// @@ -797,9 +804,9 @@ public void Execute() continue; // Fetch the current shared component value for this chunk. - var archetype = changesForChunk.Chunk->Archetype; + var archetype = ECS->GetArchetype(changesForChunk.Chunk); var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); - var sharedComponentValueArray = changesForChunk.Chunk->SharedComponentValues; + var sharedComponentValueArray = archetype->Chunks.GetSharedComponentValues(changesForChunk.Chunk.ListIndex); var sharedComponentOffset = indexInTypeArray - archetype->FirstSharedComponent; var sharedComponentDataIndex = sharedComponentValueArray[sharedComponentOffset]; var elementIndex = EntityComponentStore.GetElementIndexFromSharedComponentIndex(sharedComponentDataIndex); @@ -837,6 +844,7 @@ public void Execute() unsafe struct UpdateShadowChunksJob : IJobParallelForDefer { public TypeIndex TypeIndex; + [NativeDisableUnsafePtrRestriction] public EntityComponentStore* ECS; [ReadOnly] public NativeList Chunks; // not used, but required to be here for IJobParallelForDefer [ReadOnly] public NativeArray ChangesByChunk; [ReadOnly] public NativeArray AllocatedChunkShadowsByChunk; @@ -848,29 +856,32 @@ public void Execute(int index) var chunk = changes.Chunk; - if (null == chunk) + if (ChunkIndex.Null == chunk) return; if (!changes.Shadow.IsCreated) { // Assume the shadow was allocated correctly by the previous job. It exist in the sparse array at this chunks index. - ShadowChunksBySequenceNumber.TryAdd(chunk->SequenceNumber, AllocatedChunkShadowsByChunk[index]); + ShadowChunksBySequenceNumber.TryAdd(chunk.SequenceNumber, AllocatedChunkShadowsByChunk[index]); } else { - var archetype = chunk->Archetype; + var entityCount = chunk.Count; + var listIndex = chunk.ListIndex; + + var archetype = ECS->GetArchetype(chunk); var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, TypeIndex); - var sharedComponentValueArray = chunk->SharedComponentValues; + var sharedComponentValueArray = archetype->Chunks.GetSharedComponentValues(listIndex); var sharedComponentOffset = indexInTypeArray - archetype->FirstSharedComponent; var sharedComponentDataIndex = sharedComponentValueArray[sharedComponentOffset]; var sharedComponentElementIndex = EntityComponentStore.GetElementIndexFromSharedComponentIndex(sharedComponentDataIndex); - var entities = chunk->Buffer + archetype->Offsets[0]; + var entities = chunk.Buffer + archetype->Offsets[0]; changes.Shadow.Ptr->SharedComponentElementIndex = sharedComponentElementIndex; - changes.Shadow.Ptr->Count = changes.Chunk->Count; - changes.Shadow.Ptr->Version = changes.Chunk->GetChangeVersion(0); + changes.Shadow.Ptr->Count = entityCount; + changes.Shadow.Ptr->Version = archetype->Chunks.GetChangeVersion(0, listIndex); - UnsafeUtility.MemCpy(changes.Shadow.Ptr->Entities, entities, chunk->Count * sizeof(Entity)); + UnsafeUtility.MemCpy(changes.Shadow.Ptr->Entities, entities, entityCount * sizeof(Entity)); } } } diff --git a/Unity.Entities.Editor/Hierarchy/Model/HierarchyNameStore.cs b/Unity.Entities.Editor/Hierarchy/Model/HierarchyNameStore.cs index f7ecdf3..ad45776 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/HierarchyNameStore.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/HierarchyNameStore.cs @@ -180,7 +180,7 @@ public void GetName(in HierarchyNodeHandle handle, ref FixedString64Bytes name) } #if !DOTS_DISABLE_DEBUG_NAMES - var entityComponentStore = m_World.EntityManager.GetCheckedEntityDataAccess()->EntityComponentStore; + var entityComponentStore = m_World.EntityManager.GetCheckedEntityDataAccessExclusive()->EntityComponentStore; var entry = entityComponentStore->NameByEntity[handle.Index]; if (entry.Index != 0 && entityComponentStore->Exists(handle.ToEntity())) diff --git a/Unity.Entities.Editor/Hierarchy/Model/SubSceneMap.cs b/Unity.Entities.Editor/Hierarchy/Model/SubSceneMap.cs index 1cffa6c..3aa332f 100644 --- a/Unity.Entities.Editor/Hierarchy/Model/SubSceneMap.cs +++ b/Unity.Entities.Editor/Hierarchy/Model/SubSceneMap.cs @@ -5,6 +5,7 @@ using Unity.Scenes; using Unity.Scenes.Editor; using UnityEngine.Assertions; +using UnityEngine.Pool; using UnityEngine.SceneManagement; namespace Unity.Entities.Editor @@ -23,7 +24,8 @@ class SubSceneMap : IDisposable public void IntegrateChanges(World world, HierarchyNodeStore nodeStore, HierarchyNameStore nameStore, SubSceneChangeTracker.SubSceneMapChanges changes) { - using var _ = EditorPerformanceTrackerBridge.CreateEditorPerformanceTracker($"{nameof(SubSceneMap)}.{nameof(IntegrateChanges)}"); + using var tracker = EditorPerformanceTrackerBridge.CreateEditorPerformanceTracker($"{nameof(SubSceneMap)}.{nameof(IntegrateChanges)}"); + using var handlesToRemovePoolHandle = HashSetPool.Get(out var handlesToRemove); foreach (var sceneTag in changes.RemovedSceneTags) { @@ -32,7 +34,13 @@ public void IntegrateChanges(World world, HierarchyNodeStore nodeStore, Hierarch foreach (var sceneTag in changes.CreatedSceneTags) { + if (!world.EntityManager.Exists(sceneTag.SceneEntity) || !world.EntityManager.HasComponent(sceneTag.SceneEntity)) + continue; + var sceneEntityReference = world.EntityManager.GetComponentData(sceneTag.SceneEntity).SceneEntity; + if (!world.EntityManager.Exists(sceneEntityReference) || !world.EntityManager.HasComponent(sceneEntityReference)) + continue; + m_SceneTagToSceneReference[sceneTag] = (sceneEntityReference, world.EntityManager.GetComponentData(sceneEntityReference)); } @@ -90,8 +98,7 @@ public void IntegrateChanges(World world, HierarchyNodeStore nodeStore, Hierarch { // Entity scene must exist var handle = m_EntityScenes[entity]; - nodeStore.RemoveNode(handle); // don't remove all children - entity differ will take care of it - nameStore.RemoveName(handle); + handlesToRemove.Add(handle); } m_EntityScenes.Remove(entity); } @@ -122,14 +129,37 @@ public void IntegrateChanges(World world, HierarchyNodeStore nodeStore, Hierarch foreach (var sceneGuid in changes.RemovedSubScenes) { var handle = m_SubScenes[sceneGuid]; - nodeStore.RemoveNode(handle); - nameStore.RemoveName(handle); + handlesToRemove.Add(handle); m_SubScenes.Remove(sceneGuid); } // swap by deconstruction 🤯 (m_PreviousSceneGuidToSubScene, m_SceneGuidToSubScene) = (m_SceneGuidToSubScene, m_PreviousSceneGuidToSubScene); m_SceneGuidToSubScene.Clear(); + + // Remove unused handles + if (handlesToRemove.Count == 0) + return; + + using var poolHandle = HashSetPool.Get(out var handles); + foreach (var handle in m_SubScenes.Values) + { + handles.Add(handle); + } + + foreach (var handle in m_EntityScenes.Values) + { + handles.Add(handle); + } + + foreach (var handle in handlesToRemove) + { + if (handles.Contains(handle)) + continue; + + nodeStore.RemoveNode(handle); + nameStore.RemoveName(handle); + } } HierarchyNodeHandle AddSubScene(SubScene subScene, HierarchyNodeStore nodeStore, HierarchyNameStore nameStore) @@ -155,8 +185,13 @@ public void Dispose() var map = new NativeParallelHashMap(m_SubScenes.Count, Allocator.TempJob); foreach (var (subSceneGuid, handle) in m_SubScenes) { - var subScene = m_PreviousSceneGuidToSubScene[subSceneGuid]; - map[handle] = subScene is not null && subScene.IsLoaded; + //When laoding a new scene, the SubScene list may contains more entries than the m_PreviousSceneGuidToSubScene because + //the m_SubScene list is updated only when the SubScene change is detected. In this spurious frame, the + //m_PreviousSceneGuidToSubScene may not contains yet the guid of the new loaded sub-scenes. As such, a try-get + //here is the only possible choice, unless we keep the m_PreviousSceneGuidToSubScene and m_SubScenes in sync (that it is + //not the intent). + if (m_PreviousSceneGuidToSubScene.TryGetValue(subSceneGuid, out var subScene)) + map[handle] = subScene is not null && subScene.IsLoaded; } return map; } diff --git a/Unity.Entities.Editor/Inspector/DynamicBufferContainer.cs b/Unity.Entities.Editor/Inspector/DynamicBufferContainer.cs index 28f16db..17cdccc 100644 --- a/Unity.Entities.Editor/Inspector/DynamicBufferContainer.cs +++ b/Unity.Entities.Editor/Inspector/DynamicBufferContainer.cs @@ -23,7 +23,7 @@ static DynamicBufferContainer() EntityManager EntityManager { get; } Entity Entity { get; } - bool Exists() => EntityContainer.Exists() && EntityContainer.EntityManager.HasComponentRaw(Entity, TypeIndex); + bool Exists() => EntityContainer.Exists() && EntityContainer.World.EntityManager.HasComponentRaw(Entity, TypeIndex); BufferHeader* Header { @@ -44,7 +44,7 @@ public DynamicBufferContainer(EntityContainer entityContainer, TypeIndex typeInd IsReadOnly = readOnly; EntityContainer = entityContainer; TypeIndex = typeIndex; - EntityManager = EntityContainer.EntityManager; + EntityManager = EntityContainer.World.EntityManager; Entity = EntityContainer.Entity; } diff --git a/Unity.Entities.Editor/Inspector/EntityAspectContainer.cs b/Unity.Entities.Editor/Inspector/EntityAspectContainer.cs index 65ae105..af480f5 100644 --- a/Unity.Entities.Editor/Inspector/EntityAspectContainer.cs +++ b/Unity.Entities.Editor/Inspector/EntityAspectContainer.cs @@ -1,22 +1,17 @@ using System; using System.Collections.Generic; - -#if !NET_DOTS using System.Reflection; using Unity.Properties; -#endif namespace Unity.Entities.Editor { readonly struct EntityAspectContainer where TAspect : struct, IAspect, IAspectCreate { -#if !NET_DOTS static EntityAspectContainer() { PropertyBag.Register(new EntityAspectPropertyBag()); } -#endif public readonly World World; public readonly Entity Entity; @@ -63,7 +58,6 @@ public void Dispose() } } -#if !NET_DOTS /// /// The exposes all properties of an underlying in a safe way. /// @@ -220,5 +214,4 @@ public override void SetValue(ref EntityAspectContainer container, TVal throw new InvalidOperationException("Failed to forward aspect property."); } } -#endif } diff --git a/Unity.Entities.Editor/Inspector/EntityContainer.cs b/Unity.Entities.Editor/Inspector/EntityContainer.cs index 0237645..0b5e77c 100644 --- a/Unity.Entities.Editor/Inspector/EntityContainer.cs +++ b/Unity.Entities.Editor/Inspector/EntityContainer.cs @@ -33,28 +33,28 @@ static EntityContainer() PropertyBag.Register(new EntityContainerPropertyBag()); } - public readonly EntityManager EntityManager; + public readonly World World; public readonly Entity Entity; public readonly bool IsReadOnly; public int GetComponentCount() { - return Exists() ? EntityManager.GetComponentCount(Entity) : 0; + return Exists() ? World.EntityManager.GetComponentCount(Entity) : 0; } - public EntityContainer(EntityManager entityManager, Entity entity, bool readOnly = true) + public EntityContainer(World world, Entity entity, bool readOnly = true) { - EntityManager = entityManager; + World = world; Entity = entity; IsReadOnly = readOnly; } internal bool Exists() { - if (EntityManager == default || Entity == Entity.Null) + if (World is not { IsCreated: true } || World.EntityManager == default || Entity == Entity.Null) return false; - return EntityManager.SafeExists(Entity); + return World.EntityManager.SafeExists(Entity); } } @@ -146,13 +146,13 @@ public SharedComponentProperty(TypeIndex typeIndex, bool isReadOnly) : base(type protected override TComponent DoGetValue(ref EntityContainer container) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; return entityManager.GetSharedComponentManaged(container.Entity); } protected override void DoSetValue(ref EntityContainer container, TComponent value) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; entityManager.SetSharedComponentManaged(container.Entity, value); } } @@ -169,13 +169,13 @@ public StructComponentProperty(TypeIndex typeIndex, bool isReadOnly) : base(type protected override TComponent DoGetValue(ref EntityContainer container) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; return entityManager.GetComponentData(container.Entity); } protected override void DoSetValue(ref EntityContainer container, TComponent value) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; entityManager.SetComponentData(container.Entity, value); } } @@ -193,13 +193,13 @@ public ClassComponentProperty(TypeIndex typeIndex, bool isReadOnly) : base(typeI protected override TComponent DoGetValue(ref EntityContainer container) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; return entityManager.GetComponentData(container.Entity); } protected override void DoSetValue(ref EntityContainer container, TComponent value) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; entityManager.SetComponentData(container.Entity, value); } } @@ -217,13 +217,13 @@ public ManagedComponentProperty(TypeIndex typeIndex, bool isReadOnly) : base(typ protected override TComponent DoGetValue(ref EntityContainer container) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; return entityManager.GetComponentObject(container.Entity); } protected override void DoSetValue(ref EntityContainer container, TComponent value) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; entityManager.SetComponentObject(container.Entity, typeof(TComponent), value); } } @@ -240,13 +240,13 @@ public StructChunkComponentProperty(TypeIndex typeIndex, bool isReadOnly) : base protected override TComponent DoGetValue(ref EntityContainer container) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; return entityManager.GetChunkComponentData(container.Entity); } protected override void DoSetValue(ref EntityContainer container, TComponent value) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; entityManager.SetChunkComponentData(entityManager.GetChunk(container.Entity), value); } } @@ -264,13 +264,13 @@ public ClassChunkComponentProperty(TypeIndex typeIndex, bool isReadOnly) : base( protected override TComponent DoGetValue(ref EntityContainer container) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; return entityManager.GetChunkComponentData(container.Entity); } protected override void DoSetValue(ref EntityContainer container, TComponent value) { - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; entityManager.SetChunkComponentData(entityManager.GetChunk(container.Entity), value); } } @@ -318,7 +318,7 @@ IEnumerable> EnumerateProperties(EntityContainer cont if (!container.Exists()) yield break; - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; var count = container.GetComponentCount(); for (var i = 0; i < count; i++) { @@ -355,7 +355,7 @@ bool IIndexedProperties.TryGetProperty(ref EntityContainer cont return false; } - var entityManager = container.EntityManager; + var entityManager = container.World.EntityManager; property = GetOrCreatePropertyForType(entityManager.GetComponentTypeIndex(container.Entity, index), container.IsReadOnly); return true; } diff --git a/Unity.Entities.Editor/Inspector/EntityInspectorContext.cs b/Unity.Entities.Editor/Inspector/EntityInspectorContext.cs index cec5442..d9fbd96 100644 --- a/Unity.Entities.Editor/Inspector/EntityInspectorContext.cs +++ b/Unity.Entities.Editor/Inspector/EntityInspectorContext.cs @@ -19,7 +19,7 @@ class EntityInspectorContext : InspectionContext internal World World { get; private set; } internal EntityContainer EntityContainer { get; private set; } - internal EntityManager EntityManager => EntityContainer.EntityManager; + internal EntityManager EntityManager => EntityContainer.World.EntityManager; internal Entity Entity => EntityContainer.Entity; internal bool IsReadOnly => EntityContainer.IsReadOnly; @@ -38,12 +38,12 @@ internal void SetContext(EntitySelectionProxy proxy, bool forceWritable = false) if (forceWritable) { - EntityContainer = new EntityContainer(World.EntityManager, proxy.Entity, false); + EntityContainer = new EntityContainer(World, proxy.Entity, false); } else { var isReadonly = !EditorApplication.isPlaying || IsWorldReadOnly(World); - EntityContainer = new EntityContainer(World.EntityManager, proxy.Entity, isReadonly); + EntityContainer = new EntityContainer(World, proxy.Entity, isReadonly); } } diff --git a/Unity.Entities.Editor/Inspector/EntitySelectionProxy.cs b/Unity.Entities.Editor/Inspector/EntitySelectionProxy.cs index 3e4d22f..6bc8f4c 100644 --- a/Unity.Entities.Editor/Inspector/EntitySelectionProxy.cs +++ b/Unity.Entities.Editor/Inspector/EntitySelectionProxy.cs @@ -204,7 +204,7 @@ void CreateContext(World world) return; World = world; - Container = new EntityContainer(world.EntityManager, Entity); + Container = new EntityContainer(world, Entity); } /// Returns a hash code. @@ -219,8 +219,8 @@ public override int GetHashCode() const uint WorldNumberBitMask = (1 << 4) - 1; uint entityIndexU32 = (uint)entityIndex; - uint worldNumberU32 = (uint)World.SequenceNumber; + uint worldNumberU32 = World is not { IsCreated: true } ? 0 : (uint)World.SequenceNumber; uint entityIndexBits = entityIndexU32 & EntityBitMask; uint worldNumberBits = (worldNumberU32 & WorldNumberBitMask) << 28; diff --git a/Unity.Entities.Editor/Inspector/InspectorSettings.cs b/Unity.Entities.Editor/Inspector/InspectorSettings.cs index 63d8ef5..cbd2b82 100644 --- a/Unity.Entities.Editor/Inspector/InspectorSettings.cs +++ b/Unity.Entities.Editor/Inspector/InspectorSettings.cs @@ -11,6 +11,11 @@ class InspectorSettings : ISetting [InternalSetting] public bool DisplayComponentType = false; + string[] ISetting.GetSearchKeywords() + { + return null; + } + void ISetting.OnSettingChanged(PropertyPath path) { } diff --git a/Unity.Entities.Editor/Inspector/RelationshipsTab.cs b/Unity.Entities.Editor/Inspector/RelationshipsTab.cs index e07ae37..e800c73 100644 --- a/Unity.Entities.Editor/Inspector/RelationshipsTab.cs +++ b/Unity.Entities.Editor/Inspector/RelationshipsTab.cs @@ -106,7 +106,7 @@ internal static unsafe void GatherMatchingQueries(EntityComponentStore* ecs, Ent { var chunk = ecs->GetChunk(entity); var match = matchingArchetypes.Ptr[matchingArchetypeIndex]; - if (!chunk->MatchesFilter(match, ref query._GetImpl()->_Filter)) + if (!chunk.MatchesFilter(match, ref query._GetImpl()->_Filter)) continue; } diff --git a/Unity.Entities.Editor/Inspector/Utilities/InspectorDataModeSupport.cs b/Unity.Entities.Editor/Inspector/Utilities/InspectorDataModeSupport.cs index a6a6a03..33fedbd 100644 --- a/Unity.Entities.Editor/Inspector/Utilities/InspectorDataModeSupport.cs +++ b/Unity.Entities.Editor/Inspector/Utilities/InspectorDataModeSupport.cs @@ -180,7 +180,7 @@ static Type SelectEditor([NotNull] List targets, UnityObject contex foreach (var target in targets) { // Data mode does not apply to assets. - if (EditorUtility.IsPersistent(target)) + if (context is not EntitySelectionProxy && EditorUtility.IsPersistent(target)) return null; switch (target) diff --git a/Unity.Entities.Editor/Journaling/EntitiesJournalingSettings.cs b/Unity.Entities.Editor/Journaling/EntitiesJournalingSettings.cs index e87a2fc..09b9324 100644 --- a/Unity.Entities.Editor/Journaling/EntitiesJournalingSettings.cs +++ b/Unity.Entities.Editor/Journaling/EntitiesJournalingSettings.cs @@ -49,6 +49,11 @@ public bool PostProcess public void OnSettingChanged(PropertyPath path) { } + + public string[] GetSearchKeywords() + { + return ISetting.GetSearchKeywordsFromType(GetType()); + } } diff --git a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyComponentTypeViewList.cs b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyComponentTypeViewList.cs index 7233ef1..46560f1 100644 --- a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyComponentTypeViewList.cs +++ b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyComponentTypeViewList.cs @@ -1,3 +1,4 @@ +#if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING using System; using System.Collections; using System.Collections.Generic; @@ -66,3 +67,4 @@ public void CopyTo(Array array, int startIndex) } } } +#endif diff --git a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyEntityViewList.cs b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyEntityViewList.cs index 1a8ca15..75f8056 100644 --- a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyEntityViewList.cs +++ b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyEntityViewList.cs @@ -1,3 +1,4 @@ +#if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING using System; using System.Collections; using System.Collections.Generic; @@ -66,3 +67,4 @@ public void CopyTo(Array array, int startIndex) } } } +#endif diff --git a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyRecordViewList.cs b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyRecordViewList.cs index ed4585d..78a85dc 100644 --- a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyRecordViewList.cs +++ b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow+ReadOnlyRecordViewList.cs @@ -1,3 +1,4 @@ +#if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING using System; using System.Collections; using System.Collections.Generic; @@ -73,3 +74,4 @@ public void CopyTo(Array array, int startIndex) } } } +#endif diff --git a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow.cs b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow.cs index c04078a..851c910 100644 --- a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow.cs +++ b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindow.cs @@ -1,3 +1,4 @@ +#if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING using System; using System.Collections.Generic; using System.Linq; @@ -493,3 +494,4 @@ internal static string GetRecordDataSystemName(RecordView record) } } } +#endif diff --git a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindowDetails.cs b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindowDetails.cs index 7ec6046..a7801e8 100644 --- a/Unity.Entities.Editor/Journaling/EntitiesJournalingWindowDetails.cs +++ b/Unity.Entities.Editor/Journaling/EntitiesJournalingWindowDetails.cs @@ -1,3 +1,4 @@ +#if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING using System; using System.Collections; using System.Collections.Generic; @@ -581,3 +582,4 @@ void SelectComponent(Type componentType) } } } +#endif diff --git a/Unity.Entities.Editor/PropertyDrawers/WeakReferencePropertyDrawer.cs b/Unity.Entities.Editor/PropertyDrawers/WeakReferencePropertyDrawer.cs index 2058d9f..05d7cea 100644 --- a/Unity.Entities.Editor/PropertyDrawers/WeakReferencePropertyDrawer.cs +++ b/Unity.Entities.Editor/PropertyDrawers/WeakReferencePropertyDrawer.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using UnityEditor; using UnityEngine; using Unity.Entities.Content; @@ -110,4 +109,3 @@ class EntityPrefabReferencePropertyDrawer : WeakReferencePropertyDrawerBase public override WeakReferenceGenerationType GenerationType => WeakReferenceGenerationType.EntityPrefab; } } -#endif diff --git a/Unity.Entities.Editor/Search/JournalSearchProvider.cs b/Unity.Entities.Editor/Search/JournalSearchProvider.cs index 4870fe0..5102128 100644 --- a/Unity.Entities.Editor/Search/JournalSearchProvider.cs +++ b/Unity.Entities.Editor/Search/JournalSearchProvider.cs @@ -1,3 +1,4 @@ +#if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING using System; using System.Collections.Generic; using System.Linq; @@ -578,3 +579,4 @@ internal static void OpenProvider(string query = null) } } +#endif diff --git a/Unity.Entities.Editor/Search/SearchUtils.cs b/Unity.Entities.Editor/Search/SearchUtils.cs index 7e46e28..7ea9540 100644 --- a/Unity.Entities.Editor/Search/SearchUtils.cs +++ b/Unity.Entities.Editor/Search/SearchUtils.cs @@ -2,12 +2,9 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; -using Unity.Collections; -using UnityEditor; using UnityEditor.Search; using UnityEngine; using Unity.Editor.Bridge; -using UnityEditor.UI; using UnityEngine.UIElements; namespace Unity.Entities.Editor @@ -200,6 +197,7 @@ public override IEnumerable GetPropositions(SearchProposition } } +#if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING [QueryListBlock("Record Type", "rt", "rt", "=")] class QueryRecordTypeBlock : QueryListBlock { @@ -213,6 +211,7 @@ public override IEnumerable GetPropositions(SearchProposition return SearchUtils.GetEnumPropositions(flags, this, "Record Type:"); } } +#endif static class SearchUtils { @@ -267,10 +266,6 @@ public static Texture2D GetComponentIcon(TypeIndex typeIndex) const string k_EditorWorld = "Editor World"; const string k_DefaultWorld = "Default World"; - static string GetDefaultWorldName() - { - return EditorApplication.isPlaying ? k_DefaultWorld : k_EditorWorld; - } public static World FindWorld(string worldName = null) { @@ -282,10 +277,31 @@ public static World FindWorld(string worldName = null) else if (worldName == "editor") worldName = k_EditorWorld; } - worldName = worldName ?? GetDefaultWorldName(); - foreach (var w in World.All) - if (w.Name.Equals(worldName, System.StringComparison.InvariantCultureIgnoreCase)) - return w; + + foreach (var world in World.All) + { + var name = world.Name; + if (worldName != null) + { + if (name.Equals(worldName, StringComparison.InvariantCultureIgnoreCase)) + return world; + } + else + { + if ((world.Flags & WorldFlags.Editor) != 0) + return world; + + if ((world.Flags & WorldFlags.Game) != 0) + return world; + + if ((world.Flags & WorldFlags.GameServer) != 0) + return world; + + if ((world.Flags & WorldFlags.Live) != 0) + return world; + } + } + return null; } @@ -349,7 +365,7 @@ public static string AddOrReplaceFilterInQuery(string originalQuery, string filt var regexStr = $"\\b{filter}{op}(\\S*)"; var regex = new Regex(regexStr); - + var newQuery = regex.Replace(originalQuery, newFilter); var m = regex.Match(originalQuery); if (newQuery == originalQuery) diff --git a/Unity.Entities.Editor/Search/SystemSearchProvider.cs b/Unity.Entities.Editor/Search/SystemSearchProvider.cs index baabdd0..730c1f5 100644 --- a/Unity.Entities.Editor/Search/SystemSearchProvider.cs +++ b/Unity.Entities.Editor/Search/SystemSearchProvider.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Unity.Collections; using UnityEditor; using UnityEditor.Search; using UnityEngine; @@ -108,7 +107,6 @@ public static class SystemSearchProvider internal static Dictionary s_SystemDependencyMap = new Dictionary(); internal static WorldProxyManager s_WorldProxyManager; - internal static WorldProxy s_WorldProxy; internal static bool s_MoreTimePrecision; internal static QueryEngine s_QueryEngine; @@ -298,15 +296,11 @@ static bool SetupSystemDescriptors() { s_WorldProxyManager = new WorldProxyManager(); s_SystemDependencyMap = new(); - var world = SearchUtils.FindWorld(); - if (world == null) + s_WorldProxyManager.CreateWorldProxiesForAllWorlds(); + foreach (var updater in s_WorldProxyManager.GetAllWorldProxyUpdaters()) { - Debug.LogWarning("System Search provider: cannot find a valid World"); - return false; + updater.EnableUpdater(); } - s_WorldProxyManager.CreateWorldProxiesForAllWorlds(); - s_WorldProxy = s_WorldProxyManager.GetWorldProxyForGivenWorld(world); - s_WorldProxyManager.SelectedWorldProxy = s_WorldProxy; return true; } @@ -370,10 +364,17 @@ static IEnumerable GetAllSystems() s_SystemDependencyMap.Clear(); s_Systems.Clear(); - foreach(var system in s_WorldProxy.AllSystems) + foreach (var world in World.All) { - s_Systems.Add(new SystemDescriptor(system)); - BuildSystemDependencyMap(system); + var worldProxy = s_WorldProxyManager.GetWorldProxyForGivenWorld(world); + if (worldProxy == null) + continue; + + foreach (var system in worldProxy.AllSystems) + { + s_Systems.Add(new SystemDescriptor(system)); + BuildSystemDependencyMap(system); + } } FillSystemDependencyCache(s_Systems); diff --git a/Unity.Entities.Editor/Settings/ISetting.cs b/Unity.Entities.Editor/Settings/ISetting.cs index 12dbc04..f679d99 100644 --- a/Unity.Entities.Editor/Settings/ISetting.cs +++ b/Unity.Entities.Editor/Settings/ISetting.cs @@ -1,4 +1,8 @@ +using System.Linq; +using System.Collections.Generic; +using System.Reflection; using Unity.Properties; +using UnityEditor; namespace Unity.Entities.Editor { @@ -12,5 +16,26 @@ interface ISetting /// /// Path to the changed property. void OnSettingChanged(PropertyPath path); + + /// + /// Get the searchable keywords in this Settings group. + /// + /// + string[] GetSearchKeywords(); + + static IEnumerable GetSearchKeywordsFromProperties(System.Type type) + { + return type.GetProperties(BindingFlags.Instance | BindingFlags.Public).Select(prop => ObjectNames.NicifyVariableName(prop.Name)); + } + + static IEnumerable GetSearchKeywordsFromFields(System.Type type) + { + return type.GetFields(BindingFlags.Instance | BindingFlags.Public).Select(prop => ObjectNames.NicifyVariableName(prop.Name)); + } + + static string[] GetSearchKeywordsFromType(System.Type type) + { + return GetSearchKeywordsFromProperties(type).Concat(GetSearchKeywordsFromFields(type)).ToArray(); + } } } diff --git a/Unity.Entities.Editor/Settings/Settings.cs b/Unity.Entities.Editor/Settings/Settings.cs index bd7621b..72164c2 100644 --- a/Unity.Entities.Editor/Settings/Settings.cs +++ b/Unity.Entities.Editor/Settings/Settings.cs @@ -97,7 +97,11 @@ static void CacheSettings() s_Keywords.Add(attribute.SectionName); } - list.Add(new SettingWrapper(setting, type.GetCustomAttributes().Any())); + var wrapper = new SettingWrapper(setting, type.GetCustomAttributes().Any()); + list.Add(wrapper); + var keywords = wrapper.Setting.GetSearchKeywords(); + if (keywords != null && keywords.Length > 0) + s_Keywords.AddRange(keywords); } } } diff --git a/Unity.Entities.Editor/SystemSchedule/Utilities/SystemsWindowPreferenceSettings.cs b/Unity.Entities.Editor/SystemSchedule/Utilities/SystemsWindowPreferenceSettings.cs index a218736..a78fc74 100644 --- a/Unity.Entities.Editor/SystemSchedule/Utilities/SystemsWindowPreferenceSettings.cs +++ b/Unity.Entities.Editor/SystemSchedule/Utilities/SystemsWindowPreferenceSettings.cs @@ -14,6 +14,11 @@ void ISetting.OnSettingChanged(PropertyPath path) { } + string[] ISetting.GetSearchKeywords() + { + return ISetting.GetSearchKeywordsFromType(Configuration.GetType()); + } + [UsedImplicitly] class Inspector : PropertyInspector { diff --git a/Unity.Entities.Hybrid.HybridComponents/CompanionComponentSupportedTypes.cs b/Unity.Entities.Hybrid.HybridComponents/CompanionComponentSupportedTypes.cs index 733a5dc..1e3ae91 100644 --- a/Unity.Entities.Hybrid.HybridComponents/CompanionComponentSupportedTypes.cs +++ b/Unity.Entities.Hybrid.HybridComponents/CompanionComponentSupportedTypes.cs @@ -35,10 +35,12 @@ internal class CompanionComponentSupportedTypes typeof(CapsuleCollider), typeof(MeshCollider), #endif +#if HDRP_7_0_0_OR_NEWER || URP_7_0_0_OR_NEWER + typeof(DecalProjector), +#endif #if HDRP_7_0_0_OR_NEWER typeof(HDAdditionalLightData), typeof(HDAdditionalReflectionData), - typeof(DecalProjector), typeof(PlanarReflectionProbe), typeof(LocalVolumetricFog), #if PROBEVOLUME_CONVERSION diff --git a/Unity.Entities.Hybrid.HybridComponents/Unity.Entities.Hybrid.HybridComponents.asmdef b/Unity.Entities.Hybrid.HybridComponents/Unity.Entities.Hybrid.HybridComponents.asmdef index 70bda6d..8d221c0 100644 --- a/Unity.Entities.Hybrid.HybridComponents/Unity.Entities.Hybrid.HybridComponents.asmdef +++ b/Unity.Entities.Hybrid.HybridComponents/Unity.Entities.Hybrid.HybridComponents.asmdef @@ -14,9 +14,7 @@ "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [ - "!UNITY_DOTSRUNTIME" - ], + "defineConstraints": [], "versionDefines": [ { "name": "com.unity.render-pipelines.high-definition", @@ -70,4 +68,4 @@ } ], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/Unity.Entities.Hybrid.Tests/Baking/BakerTests.cs b/Unity.Entities.Hybrid.Tests/Baking/BakerTests.cs index c3c2b49..cb4bf49 100644 --- a/Unity.Entities.Hybrid.Tests/Baking/BakerTests.cs +++ b/Unity.Entities.Hybrid.Tests/Baking/BakerTests.cs @@ -85,6 +85,11 @@ struct ComponentTest3 : IComponentData public int Field; } + struct ComponentTest4 : IComponentData + { + public int Field; + } + struct UnmanagedSharedComponent : ISharedComponentData { public int Field; @@ -2068,6 +2073,7 @@ public class BakerTests : BakingSystemFixtureBase readonly string kNullEntityException = "InvalidOperationException: Entity Entity.Null doesn't belong to the current authoring component."; readonly System.Text.RegularExpressions.Regex kNumberedEntityException = new System.Text.RegularExpressions.Regex(@"InvalidOperationException: Entity Entity\(\d+:\d+\) doesn't belong to the current authoring component."); + readonly string kCreateAdditionalEntitiesInvalidArrayException = "ArgumentException: outputEntities is not valid or empty"; [SetUp] public override void Setup() @@ -3644,14 +3650,23 @@ public void DerivedAuthoringType_DefinedBeforeBase_IsEvaluatedAfterBase() [DisableAutoCreation] class GameObjectBaker_CreateAdditionalEntities : GameObjectBaker { - public const int AdditionalEntityCount = 3; + public static bool CreateBatched = false; + public static int AdditionalEntityCount = 3; public override void Bake(GameObject authoring) { - for (int i = 0; i < AdditionalEntityCount; ++i) + var entities = new NativeArray(AdditionalEntityCount, Allocator.Temp); + if (CreateBatched) { - var entity = CreateAdditionalEntity(TransformUsageFlags.None); - AddComponent(entity); + CreateAdditionalEntities(entities, TransformUsageFlags.None); } + else + { + for (int i = 0; i < entities.Length; ++i) + entities[i] = CreateAdditionalEntity(TransformUsageFlags.None); + } + + foreach (var entity in entities) + AddComponent(entity); } } @@ -3671,6 +3686,50 @@ public void GameObjectBaker_CreateAdditionalEntities_DoesNotThrow() } } + [Test] + public void GameObjectBaker_CreateAdditionalEntitiesBatched_DoesNotThrow() + { + using (new BakerDataUtility.OverrideBakers(true, typeof(GameObjectBaker_CreateAdditionalEntities))) + { + GameObjectBaker_CreateAdditionalEntities.CreateBatched = true; + try + { + BakingUtility.BakeGameObjects(World, new[] {m_Prefab}, m_BakingSystem.BakingSettings); + } + finally + { + GameObjectBaker_CreateAdditionalEntities.CreateBatched = false; + } + + var query = new EntityQueryBuilder(Allocator.Temp) + .WithAll() + .WithOptions(EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabledEntities) + .Build(World.EntityManager); + var entities = query.ToEntityArray(Allocator.Temp); + Assert.AreEqual(GameObjectBaker_CreateAdditionalEntities.AdditionalEntityCount, entities.Length); + } + } + + [Test] + public void GameObjectBaker_CreateAdditionalEntitiesBatched_ThrowIfCountIsZero() + { + using (new BakerDataUtility.OverrideBakers(true, typeof(GameObjectBaker_CreateAdditionalEntities))) + { + GameObjectBaker_CreateAdditionalEntities.CreateBatched = true; + GameObjectBaker_CreateAdditionalEntities.AdditionalEntityCount = 0; + try + { + UnityEngine.TestTools.LogAssert.Expect(LogType.Exception, kCreateAdditionalEntitiesInvalidArrayException); + BakingUtility.BakeGameObjects(World, new[] {m_Prefab}, m_BakingSystem.BakingSettings); + } + finally + { + GameObjectBaker_CreateAdditionalEntities.CreateBatched = false; + GameObjectBaker_CreateAdditionalEntities.AdditionalEntityCount = 3; + } + } + } + [DisableAutoCreation] class GameObjectBaker_GetGameObjectProperties : GameObjectBaker { @@ -3741,5 +3800,61 @@ public void GameObjectBaker_DisposeOfBlobAssetOwnedByBaker_Throws() Assert.Throws(() => blobRef.Dispose()); } } + + public class GetComponent_With_AbstractBaseClass : Authoring_DerivedFromAbstract { } + + public class GetComponent_With_AbstractBaseClass_Baker : Baker + { + public override void Bake(GetComponent_With_AbstractBaseClass authoring) + { + var baseclass = GetComponent(); + if (baseclass != null) + { + AddComponent(GetEntity(TransformUsageFlags.None), new ComponentTest4(){Field = baseclass.Field}); + } + } + } + + public class GetComponentInParent_With_AbstractBaseClass : MonoBehaviour { } + + public class GetComponentInParent_With_AbstractBaseClass_Baker : Baker + { + public override void Bake(GetComponentInParent_With_AbstractBaseClass authoring) + { + var baseclass = GetComponentInParent(); + if (baseclass != null) + { + AddComponent(GetEntity(TransformUsageFlags.None), new ComponentTest4(){Field = baseclass.Field}); + } + } + } + + [Test] + public void GetComponent_With_Abstract_BaseClass_DoesNotThrow() + { + m_Prefab.AddComponent().Field = 3; + using (new BakerDataUtility.OverrideBakers(true, typeof(GetComponent_With_AbstractBaseClass_Baker))) + { + Assert.DoesNotThrow(() => BakingUtility.BakeGameObjects(World, new[] {m_Prefab}, m_BakingSystem.BakingSettings)); + Assert.AreEqual(GetBakedSingleton().Field, 3); + } + } + + [Test] + public void GetComponentInParent_With_Abstract_BaseClass_DoesNotThrow() + { + m_Prefab.AddComponent().Field = 3; + GameObject child = new GameObject(); + child.transform.SetParent(m_Prefab.transform); + child.AddComponent(); + using (new BakerDataUtility.OverrideBakers(true, typeof(GetComponentInParent_With_AbstractBaseClass_Baker))) + { + Assert.DoesNotThrow(() => BakingUtility.BakeGameObjects(World, new[] {m_Prefab}, m_BakingSystem.BakingSettings)); + var testQuery = m_BakingSystem.EntityManager.CreateEntityQuery(ComponentType.ReadWrite()); + Assert.AreEqual(1, testQuery.CalculateEntityCount()); + var entity = testQuery.ToEntityArray(Allocator.Temp)[0]; + Assert.AreEqual(3, m_BakingSystem.EntityManager.GetComponentData(entity).Field); + } + } } } diff --git a/Unity.Entities.Hybrid.Tests/Baking/BakingAssemblyTests.cs b/Unity.Entities.Hybrid.Tests/Baking/BakingAssemblyTests.cs index 53f607c..1d170ac 100644 --- a/Unity.Entities.Hybrid.Tests/Baking/BakingAssemblyTests.cs +++ b/Unity.Entities.Hybrid.Tests/Baking/BakingAssemblyTests.cs @@ -1,7 +1,4 @@ using NUnit.Framework; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -#endif using Unity.Entities.Build; using Unity.Entities.Hybrid.Tests.Baking.ExcludedAssemblyTest; using Unity.Scenes.Editor.Tests; @@ -35,50 +32,6 @@ public override void TearDown() m_Settings.TearDown(); } -#if USING_PLATFORMS_PACKAGE - [Test] - public void BakingExcludeAssemblyTests() - { - m_BakingSystem = World.GetOrCreateSystemManaged(); - var manager = World.EntityManager; - - // Create the gameobject with the authoring component - var go = CreateGameObject(); - go.AddComponent(); - - // Create the a setting with a filter and another one without a filter - var normalSettings = MakeDefaultSettings(); - var exludeSettings = MakeDefaultSettings(); - var config = BuildConfiguration.CreateInstance((bs) => - { - bs.hideFlags = HideFlags.HideAndDontSave; - bs.SetComponent(new ConversionSystemFilterSettings("Unity.Entities.Hybrid.Tests.SeparateAssembly")); - }); - exludeSettings.BuildConfiguration = config; - - // Bake the gameobject - BakingUtility.BakeGameObjects(World, new[] {go}, normalSettings); - - // With regular baking it is expected that the baker runs and the component is added - var entity = m_BakingSystem.GetEntity(go); - Assert.AreEqual(true, manager.HasComponent(entity)); - - // With the filter we expect that the baker hasn't run and the component is not in the entity - BakingUtility.BakeGameObjects(World, new[] {go}, exludeSettings); - entity = m_BakingSystem.GetEntity(go); - Assert.AreEqual(false, manager.HasComponent(entity)); - - // We repeat the same two checks to make sure nothing is left that affects this - BakingUtility.BakeGameObjects(World, new[] {go}, normalSettings); - entity = m_BakingSystem.GetEntity(go); - Assert.AreEqual(true, manager.HasComponent(entity)); - - BakingUtility.BakeGameObjects(World, new[] {go}, exludeSettings); - entity = m_BakingSystem.GetEntity(go); - Assert.AreEqual(false, manager.HasComponent(entity)); - } -#endif - [Test] public void BakingExcludeAssemblyTests_BuiltInBuildsPath() { diff --git a/Unity.Entities.Hybrid.Tests/Baking/BakingSystemTests.cs b/Unity.Entities.Hybrid.Tests/Baking/BakingSystemTests.cs index 3343bab..3d72c0c 100644 --- a/Unity.Entities.Hybrid.Tests/Baking/BakingSystemTests.cs +++ b/Unity.Entities.Hybrid.Tests/Baking/BakingSystemTests.cs @@ -50,16 +50,6 @@ public override void TearDown() m_Settings.TearDown(); } - [Test] - public void CheckObjectIsNotComponent() - { - var go = CreateGameObject(); - var component = go.AddComponent(); - - Assert.DoesNotThrow(() => go.CheckObjectIsNotComponent()); - Assert.Throws(() => component.CheckObjectIsNotComponent()); - } - [Test] public void GetEntity_WithNull_Returns_NullEntity() { diff --git a/Unity.Entities.Hybrid.Tests/Conversion/BlobAssetStoreTests.cs b/Unity.Entities.Hybrid.Tests/Conversion/BlobAssetStoreTests.cs index 5ccb2b9..8e183fc 100644 --- a/Unity.Entities.Hybrid.Tests/Conversion/BlobAssetStoreTests.cs +++ b/Unity.Entities.Hybrid.Tests/Conversion/BlobAssetStoreTests.cs @@ -153,7 +153,7 @@ public void BlobValidationChecks() var nullBlob = default(BlobAssetReference); Assert.Throws(() => m_Store.TryAdd(ref tempBlob)); - Assert.Throws(() => m_Store.TryAdd(ref nullBlob)); + Assert.Throws(() => m_Store.TryAdd(ref nullBlob)); } #endif } diff --git a/Unity.Entities.Hybrid.Tests/Conversion/MultiListTests.cs b/Unity.Entities.Hybrid.Tests/Conversion/MultiListTests.cs deleted file mode 100644 index bde87d3..0000000 --- a/Unity.Entities.Hybrid.Tests/Conversion/MultiListTests.cs +++ /dev/null @@ -1,439 +0,0 @@ -using System; -using System.Linq; -using NUnit.Framework; -using Unity.Entities.Conversion; - -namespace Unity.Entities.Tests.Conversion -{ - class MultiListTests - { - MultiList> m_MultiList; - - [SetUp] - public void SetUp() - { - m_MultiList.Init(); - m_MultiList.EnsureCapacity(10); - m_MultiList.SetHeadIdsCapacity(10); - } - - [TearDown] - 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.Data.Length, Is.GreaterThanOrEqualTo(2)); - var oldHeadIds = m_MultiList.HeadIds; - var oldNext = m_MultiList.Next; - var oldData = m_MultiList.Data; - - m_MultiList.AddHead(0, "0"); - m_MultiList.AddHead(1, "1"); - - m_MultiList.ReleaseList(1); - - 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.EqualTo(oldHeadIds)); - Assert.That(m_MultiList.Next, Is.EqualTo(oldNext)); - Assert.That(m_MultiList.Data, Is.EqualTo(oldData)); - } - - [Test] - public void AddHead_WithReusedHeadId_Throws() - { - m_MultiList.AddHead(0, "0"); - Assert.Throws(() => m_MultiList.AddHead(0, "0a")); - m_MultiList.AddHead(1, "1"); - Assert.Throws(() => m_MultiList.AddHead(0, "0b")); - Assert.Throws(() => m_MultiList.AddHead(1, "1a")); - } - - [Test] - public void AddVarious_WithInvalidId_Throws() - { - const int invalid = -1; - var outOfRange = m_MultiList.HeadIds.Length; - - Assert.Throws(() => m_MultiList.AddHead(invalid, "0")); - Assert.Throws(() => m_MultiList.AddHead(outOfRange, "0")); - - Assert.Throws(() => m_MultiList.Add(invalid, "0")); - Assert.Throws(() => m_MultiList.Add(outOfRange, "0")); - - Assert.Throws(() => m_MultiList.AddTail(invalid)); - Assert.Throws(() => m_MultiList.AddTail(outOfRange)); - - Assert.Throws(() => m_MultiList.AddTail(invalid, "0")); - Assert.Throws(() => m_MultiList.AddTail(outOfRange, "0")); - - Assert.Throws(() => m_MultiList.ReleaseList(invalid)); - Assert.Throws(() => m_MultiList.ReleaseList(outOfRange)); - - Assert.Throws(() => m_MultiList.ReleaseListKeepHead(invalid)); - Assert.Throws(() => m_MultiList.ReleaseListKeepHead(outOfRange)); - - Assert.Throws(() => m_MultiList.SelectList(invalid)); - Assert.Throws(() => m_MultiList.SelectList(outOfRange)); - } - - [Test] - public unsafe void AddTailMultiple_WithoutHead_Throws() - { - Assert.Throws(() => m_MultiList.AddTail(0, null, 0)); - } - - [Test] - public unsafe void AddTailMultiple_WithZeroCount_DoesNotAdd() - { - m_MultiList.AddHead(0, "0a"); - m_MultiList.AddTail(0, null, 0); - MultiListDebugUtility.ValidateIntegrity(ref m_MultiList); - var data = MultiListDebugUtility.SelectAllData(m_MultiList); - Assert.That(data, Is.EqualTo(new[] {new[] {"0a"}})); - } - - [Test] - public unsafe void AddTailMultiple_WithSingle_AddsSingle() - { - m_MultiList.AddHead(0, "0a"); - int id; - m_MultiList.AddTail(0, &id, 1); - 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"}})); - } - - [Test] - 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.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"}})); - } - - [Test] - public void Add_BuildsNewList() - { - m_MultiList.Add(0, "0a"); // add head - m_MultiList.Add(0, "0b"); // add after head - m_MultiList.Add(0, "0c"); // add after head - m_MultiList.Add(0, "0d"); // add after head - - Assert.That(m_MultiList.HeadIds.Take(1), Is.EqualTo(new[] { 0 })); - Assert.That(m_MultiList.Next.Take(4), Is.EqualTo(new[] { 3, -1, 1, 2 })); - - var data = MultiListDebugUtility.SelectAllData(m_MultiList); - Assert.That(data, Is.EqualTo(new[] { new[] { "0a", "0d", "0c", "0b" }, })); - } - - [Test] - public void Add_WithInterleavingAddHead_ProperlyLinksLists() - { - m_MultiList.AddHead(0, "0"); - m_MultiList.Add(0, "0a"); - m_MultiList.AddHead(1, "1"); - m_MultiList.Add(0, "0b"); - m_MultiList.Add(2, "2"); - - var data = MultiListDebugUtility.SelectAllData(m_MultiList).ToList(); - - Assert.That(data.Count, Is.EqualTo(3)); - Assert.That(data[0], Is.EquivalentTo(new[] { "0", "0b", "0a" })); - Assert.That(data[1], Is.EquivalentTo(new[] { "1" })); - Assert.That(data[2], Is.EquivalentTo(new[] { "2" })); - } - - [Test] - public void AddTail_WithNoHead_Throws() - { - Assert.Throws(() => m_MultiList.AddTail(0, "0")); - - m_MultiList.AddHead(0, "0"); - Assert.DoesNotThrow(() => m_MultiList.AddTail(0, "0")); - - Assert.Throws(() => m_MultiList.AddTail(1, "1")); - } - - [Test] - public void AddTail_WithExistingHead_AddsEnd() - { - m_MultiList.AddHead(0, "0"); - m_MultiList.AddTail(0, "0a"); - m_MultiList.AddHead(1, "1"); - m_MultiList.AddTail(0, "0b"); - var serial = m_MultiList.AddTail(0, "0c").serial; - - Assert.That(serial, Is.EqualTo(3)); - Assert.That(MultiListDebugUtility.SelectAllData(m_MultiList), Is.EqualTo(new[] - { - new[] { "0", "0a", "0b", "0c" }, - new[] { "1" } - })); - } - - [Test] - public void AddTail_WithDeferredDataSet_Matches() - { - m_MultiList.AddHead(0, "0"); - m_MultiList.AddTail(0, "0a"); - var added = m_MultiList.AddTail(0); - m_MultiList.AddTail(0, "0c"); - - Assert.That(MultiListDebugUtility.SelectAllData(m_MultiList), Is.EqualTo( - new[] { new[] { "0", "0a", null, "0c" } })); - - m_MultiList.Data.Data[added.id] = "0b"; - - Assert.That(MultiListDebugUtility.SelectAllData(m_MultiList), Is.EqualTo( - new[] { new[] { "0", "0a", "0b", "0c" } })); - } - - [Test] - public void ReleaseList_WithHead_ReleasesEntireList() - { - m_MultiList.AddHead(0, "0a"); - m_MultiList.AddTail(0, "0b"); - m_MultiList.AddHead(1, "1a"); - m_MultiList.AddTail(0, "0c"); - m_MultiList.AddTail(1, "1b"); - - var released = m_MultiList.ReleaseList(0); - - Assert.That(released, Is.EqualTo(3)); - - Assert.That(m_MultiList.HeadIds.Take(2), Is.EqualTo(new[] { -1, 2 })); - - Assert.That(MultiListDebugUtility.SelectAllData(m_MultiList), Is.EqualTo( - new[] { new[] { "1a", "1b" } })); - } - - [Test] - public void ReleaseList_WithNoHead_DoesNothing() - { - m_MultiList.ReleaseList(0); - Assert.That(m_MultiList.HeadIds[0], Is.EqualTo(-1)); - - m_MultiList.Add(0, "0"); - Assert.That(m_MultiList.HeadIds[0], Is.EqualTo(0)); - - m_MultiList.ReleaseList(0); - Assert.That(m_MultiList.HeadIds[0], Is.EqualTo(-1)); - - m_MultiList.ReleaseList(0); - Assert.That(m_MultiList.HeadIds[0], Is.EqualTo(-1)); - } - - [Test] - public void ReleaseListKeepHead_WithNoHead_Throws() - { - Assert.Throws(() => m_MultiList.ReleaseListKeepHead(0)); - } - - [Test] - public void ReleaseListKeepHead_WithHead_ReleasesEntireListButKeepsHead() - { - m_MultiList.AddHead(0, "0a"); - m_MultiList.AddTail(0, "0b"); - m_MultiList.AddHead(1, "1a"); - m_MultiList.AddTail(0, "0c"); - m_MultiList.AddTail(1, "1b"); - - var released = m_MultiList.ReleaseListKeepHead(0); - - Assert.That(released, Is.EqualTo(2)); - - Assert.That(m_MultiList.HeadIds.Take(2), Is.EqualTo(new[] { 0, 2 })); - - Assert.That(MultiListDebugUtility.SelectAllData(m_MultiList), Is.EqualTo( - new[] { new[] { "0a" }, new[] { "1a", "1b" } })); - } - - [Test] - public void EnumeratorMoveNext_WithDefault_ReturnsEmpty() - { - using (var e = m_MultiList.SelectListAt(-1)) - { - Assert.That(e.MoveNext(), Is.False); - } - - using (var e = MultiListEnumerator>.Empty) - { - Assert.That(e.MoveNext(), Is.False); - } - } - - [Test] - public void EnumeratorIsEmpty_WithEmpty_ReturnsTrue() - { - Assert.IsTrue(m_MultiList.SelectListAt(-1).IsEmpty); - Assert.IsFalse(m_MultiList.SelectListAt(-1).Any); - Assert.That(m_MultiList.SelectListAt(-1), Is.Empty); - } - - [Test] - public void EnumeratorIsEmpty_WithNonEmpty_ReturnsFalse() - { - Assert.IsFalse(m_MultiList.SelectListAt(0).IsEmpty); - Assert.IsTrue(m_MultiList.SelectListAt(0).Any); - Assert.That(m_MultiList.SelectListAt(0), Is.Not.Empty); - } - - [Test] - public void EnumeratorCount() - { - Assert.That(m_MultiList.SelectListAt(-1).Count(), Is.EqualTo(0)); - - m_MultiList.Add(0, "0"); - Assert.That(m_MultiList.SelectListAt(0).Count(), Is.EqualTo(1)); - - m_MultiList.Add(0, "1"); - Assert.That(m_MultiList.SelectListAt(0).Count(), Is.EqualTo(2)); - - m_MultiList.Add(0, "2"); - Assert.That(m_MultiList.SelectListAt(0).Count(), Is.EqualTo(3)); - } - - [Test] - public void EnumeratorCurrent_WithDefault_Throws() - { - using (var e = MultiListEnumerator>.Empty) - { - // ReSharper disable once NotAccessedVariable - string s; - Assert.Throws(() => s = e.Current); - } - } - - [Test] - public void EnumeratorIteration_WithItems_ReturnsItems() - { - m_MultiList.Add(0, "0"); - - // double test to ensure no reuse of state in collection - Assert.That(m_MultiList.SelectListAt(0).Count(), Is.EqualTo(1)); - Assert.That(m_MultiList.SelectListAt(0).Count(), Is.EqualTo(1)); - - m_MultiList.Add(0, "0"); - - Assert.That(m_MultiList.SelectListAt(0).Count(), Is.EqualTo(2)); - Assert.That(m_MultiList.SelectListAt(0).Count(), Is.EqualTo(2)); - } - - [Test] - public void EnumeratorMoveNext_WithAttemptToMovePastEnd_Throws() - { - m_MultiList.Add(0, "0"); - - using (var e = m_MultiList.SelectListAt(0)) - { - Assert.That(e.MoveNext(), Is.True); - Assert.That(e.MoveNext(), Is.False); - Assert.Throws(() => e.MoveNext()); - } - } - - [Test] - public void EnumeratorCurrent_WithInvalidState_Throws() - { - m_MultiList.Add(0, "0"); - - using (var e = m_MultiList.SelectListAt(0)) - { - string s = null; - Assert.Throws(() => s = e.Current); - - e.MoveNext(); - - Assert.DoesNotThrow(() => s = e.Current); - Assert.That(s, Is.EqualTo("0")); - - e.MoveNext(); - - Assert.Throws(() => s = e.Current); - } - } - - [Test] - public void Enumerator_WithReset_RestartsIteration() - { - m_MultiList.Add(0, "0a"); - m_MultiList.Add(0, "0b"); - - using (var e = m_MultiList.SelectListAt(0)) - { - Assert.That(e.MoveNext(), Is.True); - Assert.That(e.Current, Is.EqualTo("0a")); - - e.Reset(); - - Assert.That(e.MoveNext(), Is.True); - Assert.That(e.Current, Is.EqualTo("0a")); - Assert.That(e.MoveNext(), Is.True); - Assert.That(e.Current, Is.EqualTo("0b")); - - e.Reset(); - - Assert.That(e.MoveNext(), Is.True); - Assert.That(e.Current, Is.EqualTo("0a")); - Assert.That(e.MoveNext(), Is.True); - Assert.That(e.Current, Is.EqualTo("0b")); - - Assert.That(e.MoveNext(), Is.False); - } - } - - [Test] - public void EnumeratorIterationFromHeadIdIndices_WithItems_ReturnsItems() - { - m_MultiList.Add(0, "0a"); - m_MultiList.Add(0, "0b"); - m_MultiList.Add(1, "1a"); - m_MultiList.Add(0, "0c"); - m_MultiList.Add(1, "1b"); - - using (var enumerator = m_MultiList.SelectList(0)) - { - Assert.That(enumerator, Is.EqualTo(new[] { "0a", "0c", "0b" })); - } - - using (var enumerator = m_MultiList.SelectListAt(m_MultiList.HeadIds[0])) - { - Assert.That(enumerator, Is.EqualTo(new[] { "0a", "0c", "0b" })); - } - - using (var enumerator = m_MultiList.SelectList(1)) - { - Assert.That(enumerator, Is.EqualTo(new[] { "1a", "1b" })); - } - - using (var enumerator = m_MultiList.SelectListAt(m_MultiList.HeadIds[1])) - { - Assert.That(enumerator, Is.EqualTo(new[] { "1a", "1b" })); - } - } - } -} diff --git a/Unity.Entities.Hybrid.Tests/Conversion/MultiListTests.cs.meta b/Unity.Entities.Hybrid.Tests/Conversion/MultiListTests.cs.meta deleted file mode 100644 index 61d59c6..0000000 --- a/Unity.Entities.Hybrid.Tests/Conversion/MultiListTests.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 866046c6fb5effc4cac2f058ad84bccb -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Hybrid.Tests/Conversion/UnityEngineExtensionsTests.cs b/Unity.Entities.Hybrid.Tests/Conversion/UnityEngineExtensionsTests.cs index 0637944..b430044 100644 --- a/Unity.Entities.Hybrid.Tests/Conversion/UnityEngineExtensionsTests.cs +++ b/Unity.Entities.Hybrid.Tests/Conversion/UnityEngineExtensionsTests.cs @@ -69,16 +69,5 @@ public void ComputeEntityHash_WithGameObjects() Assert.That(new[] { h01, h02, h10, h11, h20, h21, h30, h31 }, Is.Unique); } - - [Test] - public void ComputeEntityHash_WithComponent_UsesGameObject() - { - var go = CreateGameObject(); - var component = go.AddComponent(); -#pragma warning disable 0618 - Assert.That(go.ComputeInstanceHash(), Is.EqualTo(component.ComputeInstanceHash())); -#pragma warning restore 0618 - Assert.That(go.ComputeEntityGuid(0, 0), Is.EqualTo(component.ComputeEntityGuid(0, 0))); - } } } diff --git a/Unity.Entities.Hybrid.Tests/TestWorldSetup.cs b/Unity.Entities.Hybrid.Tests/TestWorldSetup.cs index 5ad0c2b..bb989a0 100644 --- a/Unity.Entities.Hybrid.Tests/TestWorldSetup.cs +++ b/Unity.Entities.Hybrid.Tests/TestWorldSetup.cs @@ -25,14 +25,11 @@ public static IEnumerable FilterSystemsToPackages(IEnumerable system } } - public static IEnumerable GetDefaultInitSystemsFromEntitiesPackage(WorldSystemFilterFlags flags) => FilterSystemsToPackages( - DefaultWorldInitialization.GetAllSystems(flags), EntitiesPackage - ); - public static World CreateEntityWorld(string name, bool isEditor) { var systems = DefaultWorldInitialization.GetAllSystemTypeIndices(WorldSystemFilterFlags.Default, true); var world = new World(name, isEditor ? WorldFlags.Editor : WorldFlags.Game); + //currently FilterSystemsToPackages filters assemblies and looks for package names, which means we have to //use the bad reflection path here. at some point, we could have a non-reflection way of doing this, but //today we don't. diff --git a/Unity.Entities.Hybrid/AssemblyInfo.cs b/Unity.Entities.Hybrid/AssemblyInfo.cs index 1df5452..5e6aeaf 100644 --- a/Unity.Entities.Hybrid/AssemblyInfo.cs +++ b/Unity.Entities.Hybrid/AssemblyInfo.cs @@ -27,6 +27,5 @@ [assembly: InternalsVisibleTo("Unity.NetCode.Editor")] [assembly: InternalsVisibleTo("Unity.Physics.PlayModeTests")] [assembly: InternalsVisibleTo("Unity.Physics.EditModeTests")] -[assembly: InternalsVisibleTo("Unity.Motion.Hybrid.Tests")] [assembly: InternalsVisibleTo("Unity.Entities.Build.Editor")] [assembly: InternalsVisibleTo("Unity.NetCode.Authoring.Hybrid")] diff --git a/Unity.Entities.Hybrid/Baking/BakeDependencies.cs b/Unity.Entities.Hybrid/Baking/BakeDependencies.cs index f0c04a8..31ca512 100644 --- a/Unity.Entities.Hybrid/Baking/BakeDependencies.cs +++ b/Unity.Entities.Hybrid/Baking/BakeDependencies.cs @@ -648,7 +648,6 @@ public void DependOnParentTransformHierarchy(Transform transform) var hashGenerator = new xxHash3.StreamingState(false); GameObject go = transform.gameObject; int goInstanceID = go.GetInstanceID(); - hashGenerator.Update(goInstanceID); // We take the dependency on the parent hierarchy. transform = transform.parent; @@ -901,6 +900,7 @@ public bool IsValid(ref SceneHierarchy hierarchy) returnValue = GetChildrenHash(ref hierarchy, GameObject, true); break; } + return (returnValue == Hash); } diff --git a/Unity.Entities.Hybrid/Baking/BakedEntityData.cs b/Unity.Entities.Hybrid/Baking/BakedEntityData.cs index 665aeb7..4e27a70 100644 --- a/Unity.Entities.Hybrid/Baking/BakedEntityData.cs +++ b/Unity.Entities.Hybrid/Baking/BakedEntityData.cs @@ -69,12 +69,12 @@ unsafe struct BakedEntityData : IDisposable // GameObject => Entity internal UnsafeParallelHashMap _GameObjectToEntity; internal EntityManager _EntityManager; - EntityArchetype _DefaultArchetype; - EntityArchetype _DefaultArchetypeAdditionalEntity; - EntityArchetype _DefaultArchetypeAdditionalEntityBakeOnly; - EntityArchetype _DefaultArchetypePrefab; - EntityArchetype _DefaultArchetypePrefabAdditionalEntity; - EntityArchetype _DefaultArchetypePrefabAdditionalEntityBakeOnly; + internal EntityArchetype _DefaultArchetype; + internal EntityArchetype _DefaultArchetypeAdditionalEntity; + internal EntityArchetype _DefaultArchetypeAdditionalEntityBakeOnly; + internal EntityArchetype _DefaultArchetypePrefab; + internal EntityArchetype _DefaultArchetypePrefabAdditionalEntity; + internal EntityArchetype _DefaultArchetypePrefabAdditionalEntityBakeOnly; EntityQuery _HasRemoveEntityInBake; EntityQuery _AllBakedQuery; EntityQuery _NewPrimaryEntitiesWithAdditionalEntities; @@ -202,6 +202,11 @@ public void Clear() _IsReferencedEntitiesDirty = true; } + public bool IsCreated + { + get { return _AuthoringIDToBakerState.IsCreated; } + } + public void ConfigureDefaultArchetypes(BakingSettings settings, Scene scene) { var types = stackalloc ComponentType[16]; @@ -270,6 +275,75 @@ public void ConfigureDefaultArchetype(ComponentType* baseType, int count, Defaul } } + //Method used by EntityBehaviour in motion + internal void ConfigureEntityBehaviourDefaultArchetypes(BakingSettings settings, Scene scene) + { + var types = stackalloc ComponentType[16]; + int count = 0; + //types[count++] = ComponentType.ReadWrite(); + if ((settings.BakingFlags & BakingUtility.BakingFlags.AddEntityGUID) != 0) + types[count++] = ComponentType.ReadWrite(); + if (settings.SceneGUID != default) + types[count++] = ComponentType.ReadWrite(); + + // all entities changed by a baker are tagged with the BakedEntity component + //types[count++] = ComponentType.ReadWrite(); + + // Archetypes for additional entities + ConfigureEntityBehaviourDefaultArchetype(types, count, DefaultArchetype.AdditionalEntity); + ConfigureEntityBehaviourDefaultArchetype(types, count, DefaultArchetype.AdditionalEntityBakeOnly); + ConfigureEntityBehaviourDefaultArchetype(types, count, DefaultArchetype.PrefabAdditionalEntity); + ConfigureEntityBehaviourDefaultArchetype(types, count, DefaultArchetype.PrefabAdditionalEntityBakeOnly); + + // AdditionalEntitiesBakingData contains a buffer of additional entities on primary entities only + // This will add AdditionalEntitiesBakingData to the default and prefab archetypes + types[count++] = ComponentType.ReadWrite(); + + ConfigureEntityBehaviourDefaultArchetype(types, count, DefaultArchetype.Default); + ConfigureEntityBehaviourDefaultArchetype(types, count, DefaultArchetype.Prefab); + + _EntityGUIDNameSpaceID = settings.NamespaceID ^ (uint) scene.handle; + _AssignEntityGUID = + (settings.BakingFlags & BakingUtility.BakingFlags.AddEntityGUID) != 0; + _SceneGUID = settings.SceneGUID; + _ConversionFlags = settings.BakingFlags; + } + + //Method used by EntityBehaviour in motion + internal void ConfigureEntityBehaviourDefaultArchetype(ComponentType* baseType, int count, DefaultArchetype defaultArchetype) + { + switch (defaultArchetype) + { + case DefaultArchetype.Default: + _DefaultArchetype = _EntityManager.CreateArchetype(baseType, count); + break; + case DefaultArchetype.AdditionalEntity: + baseType[count++] = ComponentType.ReadWrite(); + _DefaultArchetypeAdditionalEntity = _EntityManager.CreateArchetype(baseType, count); + break; + case DefaultArchetype.AdditionalEntityBakeOnly: + baseType[count++] = ComponentType.ReadWrite(); + //baseType[count++] = ComponentType.ReadWrite(); + _DefaultArchetypeAdditionalEntityBakeOnly = _EntityManager.CreateArchetype(baseType, count); + break; + case DefaultArchetype.Prefab: + baseType[count++] = ComponentType.ReadWrite(); + _DefaultArchetypePrefab = _EntityManager.CreateArchetype(baseType, count); + break; + case DefaultArchetype.PrefabAdditionalEntity: + baseType[count++] = ComponentType.ReadWrite(); + baseType[count++] = ComponentType.ReadWrite(); + _DefaultArchetypePrefabAdditionalEntity = _EntityManager.CreateArchetype(baseType, count); + break; + case DefaultArchetype.PrefabAdditionalEntityBakeOnly: + baseType[count++] = ComponentType.ReadWrite(); + baseType[count++] = ComponentType.ReadWrite(); + //baseType[count++] = ComponentType.ReadWrite(); + _DefaultArchetypePrefabAdditionalEntityBakeOnly = _EntityManager.CreateArchetype(baseType, count); + break; + } + } + public NativeList RemoveInvalidEntities(Allocator allocator) { NativeList gameObjectsNoEntity = new NativeList(100, allocator); @@ -399,9 +473,14 @@ public void ApplyBakeInstructions(ref BakeDependencies dependencies, Incremental // Create primary entity for every game objects that were created using (s_CreateEntityForGameObject.Auto()) { - foreach (var gameObject in instructions.CreatedGameObjects) + var count = instructions.CreatedGameObjects.Count; + var entities = _EntityManager.CreateEntity(_DefaultArchetype, count, Allocator.Temp); + for (int index = 0; index < count; ++index) { - var entity = CreateEntityForGameObject(gameObject, 0, _DefaultArchetype); + var gameObject = instructions.CreatedGameObjects[index]; + var entity = entities[index]; + CreateEntityForGameObject(gameObject, entity, 0); + var didAdd = _GameObjectToEntity.TryAdd(gameObject.GetInstanceID(), entity); if (!didAdd) Debug.LogError("Internally inconsistent _GameObjectToEntity table"); @@ -448,9 +527,6 @@ public void ApplyBakeInstructions(ref BakeDependencies dependencies, Incremental state.DebugState = bakerDebugState; state.BlobAssetStore = blobAssetStore; #if UNITY_EDITOR -#if USING_PLATFORMS_PACKAGE - state.BuildConfiguration = bakingSettings.BuildConfiguration; -#endif state.DotsSettings = bakingSettings.DotsSettings; #endif @@ -574,6 +650,7 @@ public void ApplyBakeInstructions(ref BakeDependencies dependencies, Incremental } else { + bool hasBaked = false; using (s_CreateBakerState.Auto()) { bakerState = new BakerState(entity, Allocator.Persistent); @@ -602,6 +679,7 @@ public void ApplyBakeInstructions(ref BakeDependencies dependencies, Incremental // baker.Baker.BakeInternal(ref tempDependencies, ref tempUsage, ref bakerState, ref _BakerDebugState, i, ref this, ref ecb, component.Component, blobAssetStore); baker.Baker.InvokeBake(state); + hasBaked = true; } catch (Exception e) { @@ -610,12 +688,15 @@ public void ApplyBakeInstructions(ref BakeDependencies dependencies, Incremental } } - using (s_RegisterDependencies.Auto()) + if (hasBaked) { - AddDependencies(ref dependencies, instanceID, ref bakerState, out var revertTransforms); - if (revertTransforms) + using (s_RegisterDependencies.Auto()) { - revertTransformsList.Add(bakerState.PrimaryEntity); + AddDependencies(ref dependencies, instanceID, ref bakerState, out var revertTransforms); + if (revertTransforms) + { + revertTransformsList.Add(bakerState.PrimaryEntity); + } } } } @@ -666,38 +747,37 @@ public void ApplyBakeInstructions(ref BakeDependencies dependencies, Incremental tempUsage.Clear(entity); // We don't bake disabled components - if (!component.Component.IsComponentDisabled()) + bool isDisabled = component.Component.IsComponentDisabled(); + + // Rebake all bakers for this component + for (int i = 0, n = bakers.Length; i < n; ++i) { - // Rebake all bakers for this component - for (int i = 0, n = bakers.Length; i < n; ++i) - { - var baker = bakers[i]; + var baker = bakers[i]; - // We don't run bake if the baker belongs to a disabled assembly - if (!baker.AssemblyData.Enabled) - continue; + // We don't run bake if the baker belongs to a disabled assembly, unless this Baker is enforcing it specifically + if (!baker.AssemblyData.Enabled || (isDisabled && !baker.ForceBakingForDisabledComponents)) + continue; - using (baker.Profiler.Auto()) + using (baker.Profiler.Auto()) + { + try { - try - { - state.Usage = &tempUsage; - state.Dependencies = &tempDependencies; - state.DebugIndex.TypeIndex = bakeTypeIndex; - state.DebugIndex.IndexInBakerArray = i; - state.BakerState = &bakerState; - state.AuthoringSource = component.Component; - state.AuthoringObject = component.Component.gameObject; - state.AuthoringId = component.Component.GetInstanceID(); - state.PrimaryEntity = entity; - - // baker.Baker.BakeInternal(ref tempDependencies, ref tempUsage, ref bakerState, ref _BakerDebugState, i, ref this, ref ecb, component.Component, blobAssetStore); - baker.Baker.InvokeBake(state); - } - catch (Exception e) - { - Debug.LogException(e); - } + state.Usage = &tempUsage; + state.Dependencies = &tempDependencies; + state.DebugIndex.TypeIndex = bakeTypeIndex; + state.DebugIndex.IndexInBakerArray = i; + state.BakerState = &bakerState; + state.AuthoringSource = component.Component; + state.AuthoringObject = component.Component.gameObject; + state.AuthoringId = component.Component.GetInstanceID(); + state.PrimaryEntity = entity; + + // baker.Baker.BakeInternal(ref tempDependencies, ref tempUsage, ref bakerState, ref _BakerDebugState, i, ref this, ref ecb, component.Component, blobAssetStore); + baker.Baker.InvokeBake(state); + } + catch (Exception e) + { + Debug.LogException(e); } } } @@ -721,43 +801,48 @@ public void ApplyBakeInstructions(ref BakeDependencies dependencies, Incremental } // We don't bake disabled components - if (!component.Component.IsComponentDisabled()) + bool isDisabled = component.Component.IsComponentDisabled(); + bool hasBaked = false; + + // Rebake all bakers for this component + for (int i = 0, n = bakers.Length; i < n; ++i) { - // Rebake all bakers for this component - for (int i = 0, n = bakers.Length; i < n; ++i) - { - var baker = bakers[i]; + var baker = bakers[i]; - // We don't run bake if the baker belongs to a disabled assembly - if (!baker.AssemblyData.Enabled) - continue; + // We don't run bake if the baker belongs to a disabled assembly, unless this Baker is enforcing it specifically + if (!baker.AssemblyData.Enabled || (isDisabled && !baker.ForceBakingForDisabledComponents)) + continue; - using (baker.Profiler.Auto()) + using (baker.Profiler.Auto()) + { + try { - try - { - state.Usage = &bakerState.Usage; - state.Dependencies = &bakerState.Dependencies; - state.DebugIndex.TypeIndex = bakeTypeIndex; - state.DebugIndex.IndexInBakerArray = i; - state.BakerState = &bakerState; - state.AuthoringSource = component.Component; - state.AuthoringObject = component.Component.gameObject; - state.AuthoringId = component.Component.GetInstanceID(); - state.PrimaryEntity = entity; - - baker.Baker.InvokeBake(state); - } - catch (Exception e) - { - Debug.LogException(e); - } + state.Usage = &bakerState.Usage; + state.Dependencies = &bakerState.Dependencies; + state.DebugIndex.TypeIndex = bakeTypeIndex; + state.DebugIndex.IndexInBakerArray = i; + state.BakerState = &bakerState; + state.AuthoringSource = component.Component; + state.AuthoringObject = component.Component.gameObject; + state.AuthoringId = component.Component.GetInstanceID(); + state.PrimaryEntity = entity; + + baker.Baker.InvokeBake(state); + hasBaked = true; + } + catch (Exception e) + { + Debug.LogException(e); } } + } + if (hasBaked) + { using (s_RegisterDependencies.Auto()) { - AddDependencies(ref dependencies, instanceID, ref bakerState, out var revertTransforms); + AddDependencies(ref dependencies, instanceID, ref bakerState, + out var revertTransforms); if (revertTransforms) { revertTransformsList.Add(bakerState.PrimaryEntity); @@ -963,13 +1048,29 @@ public void UpdateReferencedEntities() job.Add.Dispose(); } - Entity CreateEntityForGameObject(GameObject gameObject, int authoringInstanceId, EntityArchetype archetype, int serial = 0, string entityName = "") + internal Entity CreateEntityForGameObject(GameObject gameObject, int authoringInstanceId, EntityArchetype archetype, int serial = 0, string entityName = "") { if (gameObject == null) throw new ArgumentNullException(nameof(gameObject), $"{nameof(CreateEntityForGameObject)} must be called with a valid UnityEngine.Object"); var entity = _EntityManager.CreateEntity(archetype); + CreateEntityForGameObject(gameObject, entity, authoringInstanceId, serial, entityName); + return entity; + } + + private SceneSection MakeSceneSectionComponent(GameObject gameObject) + { + var section = gameObject.GetComponentInParent(true); + var sectionIndex = section != null ? section.SectionIndex : 0; + return new SceneSection { SceneGUID = _SceneGUID, Section = sectionIndex }; + } + + Entity CreateEntityForGameObject(GameObject gameObject, Entity entity, int authoringInstanceId, int serial = 0, string entityName = "") + { + if (gameObject == null) + throw new ArgumentNullException(nameof(gameObject), + $"{nameof(CreateEntityForGameObject)} must be called with a valid UnityEngine.Object"); if (_AssignEntityGUID) { @@ -977,19 +1078,8 @@ Entity CreateEntityForGameObject(GameObject gameObject, int authoringInstanceId, _EntityManager.SetComponentData(entity, entityGuid); } - //@TODO: DOTS-5445 if (_SceneGUID != default) - { - int sectionIndex = 0; - var section = gameObject.GetComponentInParent(true); - if (section != null) - { - sectionIndex = section.SectionIndex; - } - - _EntityManager.SetSharedComponent(entity, - new SceneSection {SceneGUID = _SceneGUID, Section = sectionIndex}); - } + _EntityManager.SetSharedComponent(entity, MakeSceneSectionComponent(gameObject)); #if UNITY_EDITOR var AssignName = true; @@ -1009,6 +1099,38 @@ Entity CreateEntityForGameObject(GameObject gameObject, int authoringInstanceId, return entity; } + // Batched version of CreateEntityForGameObject + void SetupEntitiesForGameObject(NativeArray entities, GameObject gameObject, int authoringInstanceId, int serial) + { + Assert.IsTrue(gameObject != null); + + if (_AssignEntityGUID) + { + var goInstanceID = gameObject.GetInstanceID(); + for (int i = 0; i < entities.Length; ++i) + { + var entityGuid = new EntityGuid(goInstanceID, authoringInstanceId, _EntityGUIDNameSpaceID, (uint)(serial + i)); + _EntityManager.SetComponentData(entities[i], entityGuid); + } + } + + if (_SceneGUID != default) + _EntityManager.SetSharedComponent(entities, MakeSceneSectionComponent(gameObject)); + +#if UNITY_EDITOR + var AssignName = true; + if (AssignName) + { + // Truncate the name to ensure it fits. + var fs = new FixedString64Bytes(); + FixedStringMethods.CopyFromTruncated(ref fs, gameObject.name); + + foreach (var e in entities) + _EntityManager.SetName(e, fs); + } +#endif + } + public bool HasAdditionalGameObjectsToBake() { return !_AdditionalGameObjectsToBake.IsEmpty; @@ -1049,8 +1171,13 @@ public void RemovePrefabRef(int instanceId) internal Entity CreateEntityForPrefab(GameObject prefab) { + var allTransforms = prefab.GetComponentsInChildren(true); + var count = allTransforms.Length; + var entities = _EntityManager.CreateEntity(_DefaultArchetypePrefab, count, Allocator.Temp); + var instanceId = prefab.GetInstanceID(); - var entity = CreateEntityForGameObject(prefab, 0, _DefaultArchetypePrefab, 0); + var entity = entities[0]; + CreateEntityForGameObject(prefab, entity, 0, 0); _GameObjectToEntity[instanceId] = entity; // Make sure prefab root is dynamic @@ -1062,16 +1189,15 @@ internal Entity CreateEntityForPrefab(GameObject prefab) _AdditionalGameObjectsToBake.Add(instanceId); // Add all children - var allTransforms = prefab.GetComponentsInChildren(true); - var linkedEntityGroupArray = new NativeArray(allTransforms.Length, Allocator.Temp); + var linkedEntityGroupArray = new NativeArray(count, Allocator.Temp); // Assign self to first position in linked entity group linkedEntityGroupArray[0] = new LinkedEntityGroupBakingData {Value = entity}; - - for(int i = 1; i < allTransforms.Length; i++) + for(int i = 1; i < count; i++) { var childGameObject = allTransforms[i].gameObject; - var childEntity = CreateEntityForGameObject(childGameObject, 0, _DefaultArchetypePrefab, 0); + var childEntity = entities[i]; + CreateEntityForGameObject(childGameObject, childEntity, 0, 0); _GameObjectToEntity[childGameObject.GetInstanceID()] = childEntity; @@ -1110,6 +1236,14 @@ public Entity GetEntity(Component component) return component == null ? Entity.Null : GetEntity(component.gameObject); } + private EntityArchetype GetAdditionalEntityArchetype(GameObject gameObject, bool bakingOnlyEntity) + { + if (gameObject.IsPrefab()) + return bakingOnlyEntity ? _DefaultArchetypePrefabAdditionalEntityBakeOnly : _DefaultArchetypePrefabAdditionalEntity; + else + return bakingOnlyEntity ? _DefaultArchetypeAdditionalEntityBakeOnly : _DefaultArchetypeAdditionalEntity; + } + public Entity CreateAdditionalEntity(GameObject gameObject, int authoringInstanceId, bool bakingOnlyEntity, string entityName = "") { var instanceId = gameObject.GetInstanceID(); @@ -1120,33 +1254,47 @@ public Entity CreateAdditionalEntity(GameObject gameObject, int authoringInstanc _ComponentToAdditionalEntityCounter[authoringInstanceId] = counter; - EntityArchetype entityArchetype; - if (gameObject.IsPrefab()) - { - if (bakingOnlyEntity) - entityArchetype = _DefaultArchetypePrefabAdditionalEntityBakeOnly; - else - entityArchetype = _DefaultArchetypePrefabAdditionalEntity; - } - else - { - if (bakingOnlyEntity) - entityArchetype = _DefaultArchetypeAdditionalEntityBakeOnly; - else - entityArchetype = _DefaultArchetypeAdditionalEntity; - } - + var entityArchetype = GetAdditionalEntityArchetype(gameObject, bakingOnlyEntity); var entity = CreateEntityForGameObject(gameObject, authoringInstanceId, entityArchetype, counter, entityName); + _EntityManager.SetComponentData(entity, new AdditionalEntityParent { Parent = primaryEntity, ParentInstanceID = instanceId }); + var buffer = _EntityManager.GetBuffer(primaryEntity); + buffer.Add(new AdditionalEntitiesBakingData + { + Value = entity, + AuthoringComponentID = authoringInstanceId + }); + + return entity; + } + + public void CreateAdditionalEntities(NativeArray outputEntities, GameObject gameObject, int authoringInstanceId, bool bakingOnlyEntity) + { + if (!outputEntities.IsCreated || outputEntities.Length == 0) + return; + + var instanceId = gameObject.GetInstanceID(); + var primaryEntity = _GameObjectToEntity[instanceId]; + + _ComponentToAdditionalEntityCounter.TryGetValue(authoringInstanceId, out var counter); + _ComponentToAdditionalEntityCounter[authoringInstanceId] = counter + outputEntities.Length; + + var entityArchetype = GetAdditionalEntityArchetype(gameObject, bakingOnlyEntity); + _EntityManager.CreateEntity(entityArchetype, outputEntities); + SetupEntitiesForGameObject(outputEntities, gameObject, authoringInstanceId, counter + 1); var buffer = _EntityManager.GetBuffer(primaryEntity); - buffer.Add(new AdditionalEntitiesBakingData() + int bufferWritePos = buffer.Length; + buffer.ResizeUninitialized(bufferWritePos + outputEntities.Length); + foreach (var e in outputEntities) + { + _EntityManager.SetComponentData(e, new AdditionalEntityParent { Parent = primaryEntity, ParentInstanceID = instanceId }); + buffer[bufferWritePos++] = new AdditionalEntitiesBakingData { - Value = entity, + Value = e, AuthoringComponentID = authoringInstanceId - }); - - return entity; + }; + } } public UnsafeHashSet GetEntitiesForBakers(Component component) @@ -1166,7 +1314,7 @@ public void UpdateTransforms(TransformAuthoringBaking transformAuthoringBaking) transformAuthoringBaking.UpdateTransforms(_GameObjectToEntity, _ReferencedEntities, ref _IsReferencedEntitiesDirty); } - private void ResetComponentAdditionalEntityCount(int authoringInstanceId, Entity entity) + internal void ResetComponentAdditionalEntityCount(int authoringInstanceId, Entity entity) { _ComponentToAdditionalEntityCounter[authoringInstanceId] = 0; if (entity != Entity.Null && _EntityManager.HasBuffer(entity)) diff --git a/Unity.Entities.Hybrid/Baking/Baker.cs b/Unity.Entities.Hybrid/Baking/Baker.cs index 42dcf3b..9912208 100644 --- a/Unity.Entities.Hybrid/Baking/Baker.cs +++ b/Unity.Entities.Hybrid/Baking/Baker.cs @@ -4,9 +4,6 @@ using System.Text; using System.Xml; #if UNITY_EDITOR -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -#endif using Unity.Entities.Build; #endif using Unity.Collections.LowLevel.Unsafe; @@ -52,9 +49,6 @@ internal struct BakerExecutionState internal BlobAssetStore BlobAssetStore; internal World World; #if UNITY_EDITOR -#if USING_PLATFORMS_PACKAGE - internal BuildConfiguration BuildConfiguration; -#endif internal IEntitiesPlayerSettings DotsSettings; #endif } @@ -1321,10 +1315,14 @@ void AddDebugTrackingForComponent(Entity entity, TypeIndex typeIndex) var bakerName = this.GetType().FullName; var previousBakers = BakerDataUtility.GetBakers(debugIndex.TypeIndex); - var previousBaker = previousBakers[debugIndex.IndexInBakerArray].Baker; - var previousBakerName = previousBaker.GetType().FullName; - var authoringComponentName = _State.AuthoringSource.GetType().FullName; - throw new InvalidOperationException($"Baking error: Attempt to add duplicate component {TypeManager.GetTypeInfo(typeIndex).DebugTypeName} for Baker {bakerName} with authoring component {authoringComponentName}. Previous component added by Baker {previousBakerName}"); + if (previousBakers != null) + { + var previousBaker = previousBakers[debugIndex.IndexInBakerArray].Baker; + var previousBakerName = previousBaker.GetType().FullName; + var authoringComponentName = _State.AuthoringSource.GetType().FullName; + throw new InvalidOperationException( + $"Baking error: Attempt to add duplicate component {TypeManager.GetTypeInfo(typeIndex).DebugTypeName} for Baker {bakerName} with authoring component {authoringComponentName}. Previous component added by Baker {previousBakerName}"); + } } /// @@ -2013,6 +2011,33 @@ public Entity CreateAdditionalEntity(TransformUsageFlags transformUsageFlags, bo return entity; } + /// + /// Creates an array of additional Entities tied to the primary entity. + /// + /// The output array which will be filled with the additional Entities created. The size of the array specifies the + /// number of additional entities to be created. The function throws if the array is not created yet or is empty. + /// The of the additional Entity. + /// Whether to mark the additional Entity as BakingOnly. + /// + /// Additional entities are automatically reverted by the baking system if the source primary entity is removed in a new baking pass. + /// Additional entities are created with the same active or static state as the Primary Entity. For example, if the authoring object is disabled, + /// the new additional entity will also have the tag component. + /// + /// Baking only additional entities are not exported in the runtime data. + /// + public void CreateAdditionalEntities(NativeArray outputEntities, TransformUsageFlags transformUsageFlags, bool bakingOnlyEntity = false) + { + if (!outputEntities.IsCreated || outputEntities.Length == 0) + throw new ArgumentException($"{nameof(outputEntities)} is not valid or empty"); + + _State.BakedEntityData->CreateAdditionalEntities(outputEntities, _State.AuthoringObject, _State.AuthoringId, bakingOnlyEntity); + foreach (var e in outputEntities) + { + _State.BakerState->Entities.Add(e); + _State.Usage->ReferencedEntityUsages.Add(new BakerEntityUsage.ReferencedEntityUsage(e, transformUsageFlags)); + } + } + /// /// Ensures that the Prefab will be baked into a Prefab and present at Runtime /// @@ -2071,24 +2096,6 @@ public bool IsBakingForEditor() } #if UNITY_EDITOR -#if USING_PLATFORMS_PACKAGE - /// - /// Get the Build Configuration Component of the GameObject - /// - /// The Build Configuration Component - /// The type of Build Configuration Component to get - /// True if the Build Configuration Component is found, False otherwise - public bool TryGetBuildConfigurationComponent(out T component) where T : Unity.Build.IBuildComponent - { - if (_State.BuildConfiguration == null) - { - component = default; - return false; - } - return _State.BuildConfiguration.TryGetComponent(out component); - } -#endif - /// /// Gets the Settings of the DOTS player /// diff --git a/Unity.Entities.Hybrid/Baking/BakerDataUtility.cs b/Unity.Entities.Hybrid/Baking/BakerDataUtility.cs index 6d20c20..b4859d7 100644 --- a/Unity.Entities.Hybrid/Baking/BakerDataUtility.cs +++ b/Unity.Entities.Hybrid/Baking/BakerDataUtility.cs @@ -47,6 +47,7 @@ public struct BakerData /// with bakers handling more components (hence handling base types) being evaluated first. /// public int CompatibleComponentCount; + public bool ForceBakingForDisabledComponents; } #if UNITY_EDITOR @@ -61,12 +62,26 @@ public static void ApplyAssemblyFilter(BakingSystemFilterSettings filter) public static BakerData[] GetBakers(TypeIndex typeIndex) { - if (!_IndexToBakerInstances.TryGetValue(typeIndex, out var bakerData)) + if (_IndexToBakerInstances == null || !_IndexToBakerInstances.TryGetValue(typeIndex, out var bakerData)) return null; return bakerData; } + //Method used by EntityBehaviour in motion + internal static int GetBakerIndexInArray(TypeIndex authoringTypeIndex, Type bakerType) + { + if (_IndexToBakerInstances == null || !_IndexToBakerInstances.TryGetValue(authoringTypeIndex, out var bakerData)) + return 0; + for (int i = 0; i < bakerData.Length; i++) + { + var data = bakerData[i]; + if (data.GetType() == bakerType) + return i; + } + return 0; + } + public static void Initialize() { if (_IndexToBakerInstances != null) @@ -190,7 +205,8 @@ static void AddBaker(Type type, IBaker baker, TypeIndex typeIndex, int compatibl Baker = baker, Profiler = new ProfilerMarker(baker.GetType().Name), AssemblyData = assemblyData, - CompatibleComponentCount = compatibleComponentCount + CompatibleComponentCount = compatibleComponentCount, + ForceBakingForDisabledComponents = type.IsDefined(typeof(ForceBakingOnDisabledComponentsAttribute)) }; _IndexToBakerInstances[typeIndex] = bakers; diff --git a/Unity.Entities.Hybrid/Baking/BakerDebugState.cs b/Unity.Entities.Hybrid/Baking/BakerDebugState.cs index d458984..d789dff 100644 --- a/Unity.Entities.Hybrid/Baking/BakerDebugState.cs +++ b/Unity.Entities.Hybrid/Baking/BakerDebugState.cs @@ -70,5 +70,10 @@ public void Clear() { addedComponentsByEntity.Clear(); } + + public bool IsCreated + { + get { return addedComponentsByEntity.IsCreated; } + } } } diff --git a/Unity.Entities.Hybrid/Baking/BakerState.cs b/Unity.Entities.Hybrid/Baking/BakerState.cs index ce27c39..2659b86 100644 --- a/Unity.Entities.Hybrid/Baking/BakerState.cs +++ b/Unity.Entities.Hybrid/Baking/BakerState.cs @@ -91,6 +91,7 @@ public void Dispose() AddedComponents.Dispose(); Entities.Dispose(); Dependencies.Dispose(); + Usage.Dispose(); #if UNITY_EDITOR ReferencedPrefabs.Dispose(); diff --git a/Unity.Entities.Hybrid/Baking/BakingAnalytics.cs b/Unity.Entities.Hybrid/Baking/BakingAnalytics.cs index 98fda20..d4981b2 100644 --- a/Unity.Entities.Hybrid/Baking/BakingAnalytics.cs +++ b/Unity.Entities.Hybrid/Baking/BakingAnalytics.cs @@ -22,6 +22,8 @@ static class BakingAnalytics const string k_EventNameOpen = "openSubScene"; const string k_EventNameImporter = "backgroundImporter"; + static readonly TypeIndex k_SkinnedMeshRendererTypeIndex; + static ProjectComplexityData s_ProjectComplexityData; static NativeList s_BakeTypeIndices; static IReadOnlyList s_BakingSystemTypes; @@ -36,12 +38,12 @@ public static void LogBakerTypeIndex(TypeIndex bakeTypeIndex) s_BakeTypeIndices.Add(bakeTypeIndex); } - public static void LogBlobAssetCount(TypeIndex blobAssetCount) + public static void LogBlobAssetCount(int blobAssetCount) { s_ProjectComplexityData.blob_assets_count = blobAssetCount; } - public static void LogPrefabCount(TypeIndex prefabCount) + public static void LogPrefabCount(int prefabCount) { s_ProjectComplexityData.prefabs_count = prefabCount; } @@ -57,8 +59,12 @@ static BakingAnalytics() custom_baking_systems_count = 0, blob_assets_count = 0, prefabs_count = 0, + skinned_mesh_renderer_component_count = 0, }; + TypeManager.Initialize(); + k_SkinnedMeshRendererTypeIndex = TypeManager.GetTypeIndex(typeof(SkinnedMeshRenderer)); + AppDomain.CurrentDomain.DomainUnload += (_, __) => { s_BakeTypeIndices.Dispose(); }; } @@ -123,6 +129,7 @@ static void SendComplexityEvent() int defaultComponentCount = 0; int customBakerCount = 0; int customBakingSystemCount = 0; + int skinnedMeshRendererComponentCount = 0; for (int i = 0; i < s_BakeTypeIndices.Length; i++) { @@ -141,9 +148,16 @@ static void SendComplexityEvent() // Log the Component/Bakers according to Assembly if (isUnityAssembly) + { defaultComponentCount++; + + if (bakeTypeIndex == k_SkinnedMeshRendererTypeIndex) + skinnedMeshRendererComponentCount++; + } else + { customBakerCount += bakers.Length; + } } @@ -167,6 +181,7 @@ static void SendComplexityEvent() s_ProjectComplexityData.default_components_count = defaultComponentCount; s_ProjectComplexityData.custom_bakers_count = customBakerCount; s_ProjectComplexityData.custom_baking_systems_count = customBakingSystemCount; + s_ProjectComplexityData.skinned_mesh_renderer_component_count = skinnedMeshRendererComponentCount; // collect max every playmode enter, send when project is closed EditorAnalytics.SendEventWithLimit(k_EventNameComplexity, s_ProjectComplexityData); @@ -192,6 +207,7 @@ struct ProjectComplexityData public int custom_baking_systems_count; public int blob_assets_count; public int prefabs_count; + public int skinned_mesh_renderer_component_count; internal void Print() { @@ -199,7 +215,8 @@ internal void Print() $"custom_bakers_count = {custom_bakers_count}, " + $"custom_baking_systems_count = {custom_baking_systems_count}, " + $"blob_assets_count = {blob_assets_count}, " + - $"prefabs_count = {prefabs_count}, "); + $"prefabs_count = {prefabs_count}, " + + $"skinned_mesh_renderer_component_count = {skinned_mesh_renderer_component_count},"); } public void Clear() @@ -209,6 +226,7 @@ public void Clear() custom_baking_systems_count = 0; blob_assets_count = 0; prefabs_count = 0; + skinned_mesh_renderer_component_count = 0; } } diff --git a/Unity.Entities.Hybrid/Baking/BakingCompanionComponentSystem.cs b/Unity.Entities.Hybrid/Baking/BakingCompanionComponentSystem.cs index 125c3f2..a74d6e4 100644 --- a/Unity.Entities.Hybrid/Baking/BakingCompanionComponentSystem.cs +++ b/Unity.Entities.Hybrid/Baking/BakingCompanionComponentSystem.cs @@ -120,14 +120,14 @@ unsafe void CreateCompanionGameObjects() } var chunk = archetypeChunk.m_Chunk; - var count = chunk->Count; - var entities = (Entity*)chunk->Buffer; + var count = chunk.Count; + var entities = (Entity*)chunk.Buffer; for (int entityIndex = 0; entityIndex < count; entityIndex++) { var entity = entities[entityIndex]; - var managedIndex = *(int*)(chunk->Buffer + (unityObjectTypeOffset + unityObjectTypeSizeOf * entityIndex)); + var managedIndex = *(int*)(chunk.Buffer + (unityObjectTypeOffset + unityObjectTypeSizeOf * entityIndex)); var obj = (UnityEngine.Component)mcs.GetManagedComponent(managedIndex); var authoringGameObject = obj.gameObject; bool wasActive = authoringGameObject.activeSelf; diff --git a/Unity.Entities.Hybrid/Baking/BakingSettings.cs b/Unity.Entities.Hybrid/Baking/BakingSettings.cs index 8b22306..51e4948 100644 --- a/Unity.Entities.Hybrid/Baking/BakingSettings.cs +++ b/Unity.Entities.Hybrid/Baking/BakingSettings.cs @@ -4,9 +4,6 @@ using Unity.Entities.Hybrid; using UnityEngine; #if UNITY_EDITOR -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -#endif using UnityEditor; using Unity.Entities.Build; using UnityEditor.AssetImporters; @@ -24,9 +21,6 @@ internal class BakingSettings public List Systems; #if UNITY_EDITOR public IEntitiesPlayerSettings DotsSettings; -#if USING_PLATFORMS_PACKAGE - public BuildConfiguration BuildConfiguration; -#endif public AssetImportContext AssetImportContext; public GameObject PrefabRoot; @@ -40,19 +34,6 @@ public BakingSystemFilterSettings BakingSystemFilterSettings if (DotsSettings != null) return DotsSettings.GetFilterSettings(); -#if USING_PLATFORMS_PACKAGE - if (BuildConfiguration != null) - { - var bakingSystemFilterSettings = new BakingSystemFilterSettings(); - if (BuildConfiguration.TryGetComponent(out var conversionSystemFilterSettings)) - { - bakingSystemFilterSettings.ExcludedBakingSystemAssemblies = - conversionSystemFilterSettings.ExcludedConversionSystemAssemblies; - } - return bakingSystemFilterSettings; - } -#endif - return null; } } diff --git a/Unity.Entities.Hybrid/Baking/BakingUtility.cs b/Unity.Entities.Hybrid/Baking/BakingUtility.cs index 2ebeb53..856d781 100644 --- a/Unity.Entities.Hybrid/Baking/BakingUtility.cs +++ b/Unity.Entities.Hybrid/Baking/BakingUtility.cs @@ -104,12 +104,12 @@ static void PreprocessBake(World conversionWorld, BakingSettings settings) var systemTypeIndices = DefaultWorldInitialization.GetAllSystemTypeIndices(settings.FilterFlags); //currently, baking uses reflection to decide whether to run a system, so we can't avoid this. but we - //should fix that. + //should fix that. var typesList = new List(); for (int i=0; i 0) { systemTypes.AddRange(settings.ExtraSystems); @@ -203,7 +204,7 @@ public bool IsRootGroup(SystemTypeIndex type) => type == TypeManager.GetSystemTypeIndex(); } - static void AddBakingSystems(World gameObjectWorld, IEnumerable systemTypes) + internal static void AddBakingSystems(World gameObjectWorld, IEnumerable systemTypes) { var bakeSystemGroup = gameObjectWorld.GetOrCreateSystemManaged(); var postBakingSystemGroup = gameObjectWorld.GetOrCreateSystemManaged(); @@ -217,7 +218,7 @@ static void AddBakingSystems(World gameObjectWorld, IEnumerable systemType } DefaultWorldInitialization.AddSystemToRootLevelSystemGroupsInternal(gameObjectWorld, systemTypeIndices, bakeSystemGroup, new BakingRootGroups()); - + bakeSystemGroup.SortSystems(); postBakingSystemGroup.SortSystems(); transformBakingSystemGroup.SortSystems(); diff --git a/Unity.Entities.Hybrid/Baking/ForceBakingOnDisabledComponentsAttribute.cs b/Unity.Entities.Hybrid/Baking/ForceBakingOnDisabledComponentsAttribute.cs new file mode 100644 index 0000000..07ad2d7 --- /dev/null +++ b/Unity.Entities.Hybrid/Baking/ForceBakingOnDisabledComponentsAttribute.cs @@ -0,0 +1,11 @@ +using System; +using UnityEngine; + +namespace Unity.Entities +{ + /// + /// Attribute that informs the baking system that bakers need to be run on the authoring component + /// even when the authoring component is disabled. + /// + internal class ForceBakingOnDisabledComponentsAttribute : Attribute { } +} diff --git a/Unity.Entities.Hybrid/Baking/ForceBakingOnDisabledComponentsAttribute.cs.meta b/Unity.Entities.Hybrid/Baking/ForceBakingOnDisabledComponentsAttribute.cs.meta new file mode 100644 index 0000000..84b3bc7 --- /dev/null +++ b/Unity.Entities.Hybrid/Baking/ForceBakingOnDisabledComponentsAttribute.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 9f591333c66c404cbd49ca683cec02b8 +timeCreated: 1682693205 \ No newline at end of file diff --git a/Unity.Entities.Hybrid/Baking/GameObjectComponents.cs b/Unity.Entities.Hybrid/Baking/GameObjectComponents.cs index 60bf56c..395061b 100644 --- a/Unity.Entities.Hybrid/Baking/GameObjectComponents.cs +++ b/Unity.Entities.Hybrid/Baking/GameObjectComponents.cs @@ -27,54 +27,68 @@ public ComponentData(Component component) //UnsafeParallelMultiHashMap _TransformData; // GameObject InstanceID -> Component Type + component InstanceID // Used to keep the last converted state of each game object. - UnsafeParallelMultiHashMap _GameObjectComponentMetaData; + UnsafeParallelHashMap> _GameObjectComponentMetaData; public GameObjectComponents(Allocator allocator) { - _GameObjectComponentMetaData = new UnsafeParallelMultiHashMap(1024, allocator); + _GameObjectComponentMetaData = new UnsafeParallelHashMap>(1024, allocator); } - public UnsafeParallelMultiHashMap.Enumerator GetComponents(int instanceID) + public UnsafeList.ReadOnly GetComponents(int instanceID) { - return _GameObjectComponentMetaData.GetValuesForKey(instanceID); + _GameObjectComponentMetaData.TryGetValue(instanceID, out var componentList); + return componentList.AsReadOnly(); } public bool HasComponent (int gameObjectInstanceID, int componentInstanceID) { - foreach(var com in _GameObjectComponentMetaData.GetValuesForKey(gameObjectInstanceID)) + if (_GameObjectComponentMetaData.TryGetValue(gameObjectInstanceID, out var componentList)) { - if (com.InstanceID == componentInstanceID) - return true; + foreach (var com in componentList) + { + if (com.InstanceID == componentInstanceID) + return true; + } } + return false; } public int GetComponent (int gameObjectInstanceID, TypeIndex componentType) { - foreach(var com in _GameObjectComponentMetaData.GetValuesForKey(gameObjectInstanceID)) + if (_GameObjectComponentMetaData.TryGetValue(gameObjectInstanceID, out var componentList)) { - if (com.TypeIndex == componentType || TypeManager.IsDescendantOf(com.TypeIndex, componentType)) - return com.InstanceID; + foreach (var com in componentList) + { + if (com.TypeIndex == componentType || TypeManager.IsDescendantOf(com.TypeIndex, componentType)) + return com.InstanceID; + } } return 0; } public void GetComponents (int gameObjectInstanceID, TypeIndex componentType, ref UnsafeList results) { - foreach(var com in _GameObjectComponentMetaData.GetValuesForKey(gameObjectInstanceID)) + if (_GameObjectComponentMetaData.TryGetValue(gameObjectInstanceID, out var componentList)) { - if (com.TypeIndex == componentType || TypeManager.IsDescendantOf(com.TypeIndex, componentType)) - results.Add(com.InstanceID); + foreach (var com in componentList) + { + if (com.TypeIndex == componentType || TypeManager.IsDescendantOf(com.TypeIndex, componentType)) + results.Add(com.InstanceID); + } } } private void GetComponentsHash(int gameObjectInstanceID, TypeIndex componentType, ref xxHash3.StreamingState hash) { - foreach(var com in _GameObjectComponentMetaData.GetValuesForKey(gameObjectInstanceID)) + if (_GameObjectComponentMetaData.TryGetValue(gameObjectInstanceID, out var componentList)) { - if (com.TypeIndex == componentType || TypeManager.IsDescendantOf(com.TypeIndex, componentType)) - hash.Update(com.InstanceID); + foreach (var com in componentList) + { + if (com.TypeIndex == componentType || TypeManager.IsDescendantOf(com.TypeIndex, componentType)) + hash.Update(com.InstanceID); + } } } @@ -115,9 +129,15 @@ public static int GetComponentInParent(ref GameObjectComponents components, ref public void AddGameObject(GameObject gameObject, List components) { var instanceID = gameObject.GetInstanceID(); - + if (!_GameObjectComponentMetaData.TryGetValue(instanceID, out var componentDataList)) + { + componentDataList = new UnsafeList(components.Count, Allocator.Persistent); + } foreach (var com in components) - _GameObjectComponentMetaData.Add(instanceID, new ComponentData(com)); + { + componentDataList.Add(new ComponentData(com)); + } + _GameObjectComponentMetaData[instanceID] = componentDataList; } private static int GetComponentInChildrenInternal(ref GameObjectComponents components, ref SceneHierarchy hierarchy, int index, TypeIndex type) @@ -263,45 +283,60 @@ public unsafe bool UpdateGameObject(GameObject gameObject, List curre //TODO: DOTS-5453 var instanceID = gameObject.GetInstanceID(); + int removedCount = 0; - // Find the components that were added, and thus need to be baked - foreach (var newComponent in currentComponentsOnGameObject) + if (_GameObjectComponentMetaData.TryGetValue(instanceID, out var componentDataList)) { - bool found = false; - foreach (var oldComponent in _GameObjectComponentMetaData.GetValuesForKey (instanceID)) + // Record added components, that need to be baked + foreach (var newComponent in currentComponentsOnGameObject) { - if (oldComponent.InstanceID == newComponent.GetInstanceID()) + bool found = false; + foreach (var oldComponent in componentDataList) { - outExistingComponents.Add(newComponent); - found = true; - break; + if (oldComponent.InstanceID == newComponent.GetInstanceID()) + { + outExistingComponents.Add(newComponent); + found = true; + break; + } } - } - if (!found) - outAddedComponents.Add(newComponent); - } + if (!found) + outAddedComponents.Add(newComponent); + } - foreach (var oldComponent in _GameObjectComponentMetaData.GetValuesForKey (instanceID)) - { - bool found = false; - foreach (var newComponent in currentComponentsOnGameObject) + // Record removed components + foreach (var oldComponent in componentDataList) { - if (oldComponent.InstanceID == newComponent.GetInstanceID()) + bool found = false; + foreach (var newComponent in currentComponentsOnGameObject) { - found = true; - break; + if (oldComponent.InstanceID == newComponent.GetInstanceID()) + { + found = true; + break; + } } + + if (!found) + removed.Add(oldComponent.InstanceID); } - if (!found) - removed.Add(oldComponent.InstanceID); + removedCount = componentDataList.Length; + componentDataList.Clear(); + } + else + { + // There are no previous components recorded, so all current are new + outAddedComponents.AddRange(currentComponentsOnGameObject); + componentDataList = new UnsafeList(currentComponentsOnGameObject.Count, Allocator.Persistent); } - int gameObjectInstanceID = gameObject.GetInstanceID(); - var removedCount = _GameObjectComponentMetaData.Remove(gameObjectInstanceID); foreach (var com in currentComponentsOnGameObject) - _GameObjectComponentMetaData.Add(instanceID, new ComponentData(com)); + { + componentDataList.Add(new ComponentData(com)); + } + _GameObjectComponentMetaData[instanceID] = componentDataList; return removedCount == 0; } @@ -313,13 +348,27 @@ public unsafe bool UpdateGameObject(GameObject gameObject, List curre /// Returns true if the game object was still alive. public bool DestroyGameObject(int gameObjectInstanceID) { - return _GameObjectComponentMetaData.Remove(gameObjectInstanceID) != 0; + int length = 0; + if (_GameObjectComponentMetaData.TryGetValue(gameObjectInstanceID, out var componentDataList)) + { + length = componentDataList.Length; + componentDataList.Dispose(); + _GameObjectComponentMetaData.Remove(gameObjectInstanceID); + } + return length != 0; } public void Dispose() { if (_GameObjectComponentMetaData.IsCreated) + { + // We need to release the individual lists + foreach (var list in _GameObjectComponentMetaData.GetValueArray(Allocator.Temp)) + { + list.Dispose(); + } _GameObjectComponentMetaData.Dispose(); + } } } } diff --git a/Unity.Entities.Hybrid/Baking/IncrementalBakingChangeTracker.cs b/Unity.Entities.Hybrid/Baking/IncrementalBakingChangeTracker.cs index 0f2ebea..d3c79d9 100644 --- a/Unity.Entities.Hybrid/Baking/IncrementalBakingChangeTracker.cs +++ b/Unity.Entities.Hybrid/Baking/IncrementalBakingChangeTracker.cs @@ -82,38 +82,6 @@ internal void FillBatch(ref IncrementalBakingBatch batch) ValidComponents.AddRange(ComponentChanges); } - internal void FillBatch(ref IncrementalConversionBatch batch) - { - batch.DeletedInstanceIds = DeletedInstanceIds.AsArray(); - batch.ChangedInstanceIds = ChangedInstanceIds.ToNativeArray(Allocator.Temp); - - // This is how Conversion expects the data, in one array, so we compose it to that - // We don't care that this is a bit sloppy, because we just want Conversion to work for comparison, not performance - int bakeHierarchyInstanceIdCount = BakeHierarchyInstanceIds.Count(); - int forceBakeHierarchyInstanceIdCount = ForceBakeHierarchyInstanceIds.Count(); - - var reconvertHierarchyInstanceIds = new NativeArray(bakeHierarchyInstanceIdCount + forceBakeHierarchyInstanceIdCount, Allocator.Temp); - - int count = 0; - foreach(var id in BakeHierarchyInstanceIds) - { - reconvertHierarchyInstanceIds[count++] = id; - } - - foreach(var id in ForceBakeHierarchyInstanceIds) - { - reconvertHierarchyInstanceIds[count++] = id; - } - - batch.ReconvertHierarchyInstanceIds = reconvertHierarchyInstanceIds; - batch.ParentChangeInstanceIds = ParentChangeInstanceIds; - batch.ChangedAssets = ChangedAssets.AsArray(); - batch.DeletedAssets = DeletedAssets.AsArray(); - batch.ChangedComponents = ValidComponents; - ValidComponents.AddRange(ComponentChanges); - } - - public void Dispose() { if (DeletedInstanceIds.IsCreated) diff --git a/Unity.Entities.Hybrid/Baking/TransformUsageFlags.cs b/Unity.Entities.Hybrid/Baking/TransformUsageFlags.cs index c9dc93a..039965a 100644 --- a/Unity.Entities.Hybrid/Baking/TransformUsageFlags.cs +++ b/Unity.Entities.Hybrid/Baking/TransformUsageFlags.cs @@ -3,10 +3,10 @@ namespace Unity.Entities { /// - /// Controls how Transform components on GameObjects are converted to entity data. + /// Controls how Transform components on GameObjects are converted to entity data. /// /// - /// These flags help to reduce the number of unnecessary transform components in baked entities based on their + /// These flags help to reduce the number of unnecessary transform components in baked entities based on their /// intended use at runtime. /// /// The Dynamic flag replicates as close as possible the GameObject data and structure. @@ -44,8 +44,7 @@ namespace Unity.Entities /// takes off, the hierarchy needs to be broken manually at runtime and the transform data converted to world /// space. /// - /// If the helicopter can shoot some bullets, the bullet entity prefab should be marked as Dynamic so it can be - /// instantiated at the right position and moved. + /// If the helicopter can shoot some bullets, the bullet entity prefab will automatically be marked as Dynamic so the instances can be placed in the world. /// /// There is also a case where an Entity might be stripped out from the final world during baking. This happens /// when there is no baker adding a TransformUsageFlag to it (TranformUsageFlags.None counts as adding a @@ -60,7 +59,7 @@ namespace Unity.Entities public enum TransformUsageFlags : int { /// - /// Specifies that the entity doesn't need transform components. + /// Specifies that the entity doesn't need transform components. /// /// /// Unless something else requests other flags, this entity doesn't have any transform related components and isn't part of a hierarchy. @@ -71,7 +70,7 @@ public enum TransformUsageFlags : int None = 0, /// - /// Indicates that an entity requires the necessary transform components to be rendered (), + /// Indicates that an entity requires the necessary transform components to be rendered (), /// but it doesn't require the transform components needed to move the entity at runtime. /// /// @@ -90,7 +89,7 @@ public enum TransformUsageFlags : int Dynamic = 1 << 1, /// - /// Indicates that an entity needs to be in world space, even if they have a Dynamic entity as a parent. + /// Indicates that an entity needs to be in world space, even if they have a Dynamic entity as a parent. /// /// /// This means that an entity doesn't have a component and all their @@ -100,7 +99,7 @@ public enum TransformUsageFlags : int WorldSpace = 1 << 2, /// - /// Indicates that an entity requires transform components to represent non uniform scale. + /// Indicates that an entity requires transform components to represent non uniform scale. /// /// /// For Dynamic entities, all the scale information is stored in a component. diff --git a/Unity.Entities.Hybrid/GameObjectConversion/ConversionSystemFilterSettings.cs b/Unity.Entities.Hybrid/GameObjectConversion/ConversionSystemFilterSettings.cs deleted file mode 100644 index d7c1c1f..0000000 --- a/Unity.Entities.Hybrid/GameObjectConversion/ConversionSystemFilterSettings.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Unity.Properties; -using Assembly = System.Reflection.Assembly; - -namespace Unity.Entities.Conversion -{ -#if UNITY_EDITOR -#if USING_PLATFORMS_PACKAGE - public sealed class ConversionSystemFilterSettings : Unity.Build.IBuildComponent - { - HashSet m_ExcludedDomainAssemblies; - - // this must be initialized to true, so that when properties does a transfer - // and updates the List property, we get a chance to tell m_ConversionTypeCache - // about the change. - bool m_IsDirty = true; - - [CreateProperty] - public List> ExcludedConversionSystemAssemblies { get; set; } = - new List>(); - - public ConversionSystemFilterSettings() {} - - public ConversionSystemFilterSettings(params string[] excludedAssemblyDefinitionNames) - { - foreach (var name in excludedAssemblyDefinitionNames) - { - var asset = FindAssemblyDefinitionAssetByName(name); - if (asset != null && asset) - { - ExcludedConversionSystemAssemblies.Add(asset); - } - } - } - - public ConversionSystemFilterSettings(params UnityEditorInternal.AssemblyDefinitionAsset[] excludedAssemblyDefinitionAssets) - { - foreach (var asset in excludedAssemblyDefinitionAssets) - { - if (asset != null && asset) - { - ExcludedConversionSystemAssemblies.Add(asset); - } - } - } - - public UnityEditorInternal.AssemblyDefinitionAsset FindAssemblyDefinitionAssetByName(string name) - { - var assetPath = UnityEditor.AssetDatabase.FindAssets($"t: asmdef {name}") - .Select(UnityEditor.AssetDatabase.GUIDToAssetPath) - .FirstOrDefault(x => Path.GetFileNameWithoutExtension(x) == name); - return UnityEditor.AssetDatabase.LoadAssetAtPath(assetPath); - } - - public bool ShouldRunConversionSystem(Type type) - { - UpdateIfDirty(); - if (m_ExcludedDomainAssemblies == null) - return true; - - return !m_ExcludedDomainAssemblies.Contains(type.Assembly); - } - - public bool IsAssemblyExcluded(Assembly assembly) - { - UpdateIfDirty(); - if (m_ExcludedDomainAssemblies == null) - return false; - - return m_ExcludedDomainAssemblies.Contains(assembly); - } - - public void SetDirty() - { - m_IsDirty = true; - } - - void UpdateIfDirty() - { - if (!m_IsDirty) - return; - - if (ExcludedConversionSystemAssemblies.Count == 0) - { - m_ExcludedDomainAssemblies = null; - return; - } - - m_ExcludedDomainAssemblies = new HashSet(); - - var domainAssemblies = AppDomain.CurrentDomain.GetAssemblies(); - foreach (var excl in ExcludedConversionSystemAssemblies.Select(lazy => lazy.asset)) - { - var asm = domainAssemblies.FirstOrDefault(s => s.GetName().Name == excl.name); - if (asm != null) - m_ExcludedDomainAssemblies.Add(asm); - } - - m_IsDirty = false; - } - } -#endif -#endif -} diff --git a/Unity.Entities.Hybrid/GameObjectConversion/Incremental/DependencyTracker.cs b/Unity.Entities.Hybrid/GameObjectConversion/Incremental/DependencyTracker.cs deleted file mode 100644 index 4f92466..0000000 --- a/Unity.Entities.Hybrid/GameObjectConversion/Incremental/DependencyTracker.cs +++ /dev/null @@ -1,165 +0,0 @@ -using System; -using Unity.Burst; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using UnityEngine; - -namespace Unity.Entities -{ - /// - /// Allows you to query dependents for specific component types. - /// - /// ATTENTION: This is future public API. - /// - [BurstCompile] - [GenerateTestsForBurstCompatibility] - internal struct DependencyTracker : IDisposable - { - UnsafeParallelMultiHashMap _dependentsByInstanceId; - UnsafeParallelMultiHashMap _dependenciesByInstanceId; - - internal DependencyTracker(Allocator allocator) - { - _dependentsByInstanceId = new UnsafeParallelMultiHashMap(0, allocator); - _dependenciesByInstanceId = new UnsafeParallelMultiHashMap(0, allocator); - } - - public void Dispose() - { - if (_dependentsByInstanceId.IsCreated) - _dependentsByInstanceId.Dispose(); - if (_dependenciesByInstanceId.IsCreated) - _dependenciesByInstanceId.Dispose(); - } - - internal void ClearDependencies(NativeArray instanceIds) - { - for (int i = 0; i < instanceIds.Length; i++) - { - int id = instanceIds[i]; - var iter = _dependenciesByInstanceId.GetValuesForKey(id); - while (iter.MoveNext()) - _dependentsByInstanceId.Remove(iter.Current, id); - _dependenciesByInstanceId.Remove(id); - } - } - - internal void AddDependency(int dependentId, int dependsOnId) - { - _dependentsByInstanceId.Add(dependsOnId, dependentId); - _dependenciesByInstanceId.Add(dependentId, dependsOnId); - } - - internal void RemapInstanceId(int previousId, int newId) - { - var dependents = new UnsafeList(0, Allocator.Temp); - foreach (var v in _dependentsByInstanceId.GetValuesForKey(previousId)) - { - dependents.Add(v); - _dependenciesByInstanceId.Remove(v, previousId); - _dependenciesByInstanceId.Add(v, newId); - } - - for (int i = 0; i < dependents.Length; i++) - _dependentsByInstanceId.Add(newId, dependents[i]); - _dependentsByInstanceId.Remove(previousId); - } - - /// - /// Returns whether a given instance has any dependents registered to it. - /// - /// The instance to query for dependents. - /// True if there are any dependents, false otherwise. - public bool HasDependents(int instanceId) => _dependentsByInstanceId.ContainsKey(instanceId); - - internal NativeArray GetAllDependencies(Allocator allocator) => _dependentsByInstanceId.GetKeyArray(allocator); - internal UnsafeParallelMultiHashMap.Enumerator GetAllDependents(int instanceId) => _dependentsByInstanceId.GetValuesForKey(instanceId); - internal bool HasDependencies() => !_dependentsByInstanceId.IsEmpty; - internal bool HasDependencies(int instanceId) => _dependentsByInstanceId.ContainsKey(instanceId); - - internal unsafe void CalculateDependents(NativeArray instanceIds, NativeParallelHashSet outDependents) - { - var toBeProcessed = new UnsafeList(0, Allocator.Temp); - toBeProcessed.AddRange(instanceIds.GetUnsafeReadOnlyPtr(), instanceIds.Length); - while (toBeProcessed.Length != 0) - { - var instance = toBeProcessed.Ptr[toBeProcessed.Length - 1]; - toBeProcessed.RemoveAtSwapBack(toBeProcessed.Length - 1); - if (outDependents.Add(instance)) - { - var dependentIds = _dependentsByInstanceId.GetValuesForKey(instance); - while (dependentIds.MoveNext()) - { - if (!outDependents.Contains(dependentIds.Current)) - toBeProcessed.Add(dependentIds.Current); - } - } - } - } - - /// - /// Calculate all direct dependents for a given set of instances. Transitive dependents are not returned. - /// - /// The instance ids whose dependents should be collected - /// The hash set to add the dependents to. - public void CalculateDirectDependents(NativeArray instanceIds, NativeParallelHashSet outDependents) - { - for (int i = 0; i < instanceIds.Length; i++) - { - var dependents = _dependentsByInstanceId.GetValuesForKey(instanceIds[i]); - while (dependents.MoveNext()) - outDependents.Add(dependents.Current); - } - } - - /// - /// Calculate all direct dependents for a given set of instances. Transitive dependents are not returned. - /// - /// The instance ids whose dependents should be collected - /// The list to add the dependents to. - public void CalculateDirectDependents(NativeArray instanceIds, NativeList outDependents) - { - for (int i = 0; i < instanceIds.Length; i++) - { - var dependents = _dependentsByInstanceId.GetValuesForKey(instanceIds[i]); - while (dependents.MoveNext()) - outDependents.Add(dependents.Current); - } - } - - /// - /// Calculate all direct dependents for a given set of instances. Transitive dependents are not returned. - /// This method is asynchronous and returns a job handle that you can use to chain further jobs. - /// - /// The instance ids whose dependents should be collected. - /// The list to add the dependents to. - /// A JobHandle that will be treated as a dependency for all jobs scheduled by this function. - /// A JobHandle for the jobs scheduled by ths function. - public JobHandle CalculateDirectDependentsAsync(NativeArray instanceIds, NativeList outDependents, JobHandle dependency=default) - { - return new CollectDependencies - { - Dependencies = this, - OutputIds = outDependents, - ChangedInstanceIds = instanceIds - }.Schedule(dependency); - } - - [BurstCompile] - struct CollectDependencies : IJob - { - [ReadOnly] - public DependencyTracker Dependencies; - [WriteOnly] - public NativeList OutputIds; - [ReadOnly] - public NativeArray ChangedInstanceIds; - - public void Execute() - { - Dependencies.CalculateDirectDependents(ChangedInstanceIds, OutputIds); - } - } - } -} diff --git a/Unity.Entities.Hybrid/GameObjectConversion/Incremental/DependencyTracker.cs.meta b/Unity.Entities.Hybrid/GameObjectConversion/Incremental/DependencyTracker.cs.meta deleted file mode 100644 index 912a0fd..0000000 --- a/Unity.Entities.Hybrid/GameObjectConversion/Incremental/DependencyTracker.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 85f783dcec4e4798a327b9dbdac1ecdc -timeCreated: 1588859487 \ No newline at end of file diff --git a/Unity.Entities.Hybrid/GameObjectConversion/Incremental/IncrementalConversionChangeTracker.cs b/Unity.Entities.Hybrid/GameObjectConversion/Incremental/IncrementalConversionChangeTracker.cs deleted file mode 100644 index aa3f8e6..0000000 --- a/Unity.Entities.Hybrid/GameObjectConversion/Incremental/IncrementalConversionChangeTracker.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Unity.Collections; -using UnityEditor; -using UnityEngine; - -namespace Unity.Entities -{ - /// - /// Represents a fine-grained description of changes that happened since the last conversion. - /// - internal struct IncrementalConversionBatch : IDisposable - { - /// - /// Instance IDs of all GameObjects that were deleted. - /// Note that this can overlap with any of the other collections. - /// - public NativeArray DeletedInstanceIds; - - /// - /// Instance IDs of all GameObjects that were changed. - /// /// Note that this might include IDs of destroyed GameObjects. - /// - public NativeArray ChangedInstanceIds; - - /// - /// Instance IDs of all GameObjects that should have the entire hierarchy below them reconverted. - /// Note that this might include IDs of destroyed GameObjects. - /// - public NativeArray ReconvertHierarchyInstanceIds; - - /// - /// Maps instance IDs of GameObjects to the instance ID of their last recorded parent if the parenting changed. - /// Note that this might included instance IDs of destroyed GameObjects on either side. - /// - public NativeParallelHashMap ParentChangeInstanceIds; - - /// - /// Contains the instance IDs of all assets that were changed since the last conversion. - /// - public NativeArray ChangedAssets; - - /// - /// Contains the GUIDs of all assets that were deleted since the last conversion. - /// - public NativeArray DeletedAssets; - - /// - /// Contains a list of all components that were changed since the last conversion. Note that the components - /// might have been destroyed in the mean time. - /// - public List ChangedComponents; - - public void Dispose() - { - DeletedInstanceIds.Dispose(); - ChangedInstanceIds.Dispose(); - ReconvertHierarchyInstanceIds.Dispose(); - ParentChangeInstanceIds.Dispose(); - ChangedAssets.Dispose(); - DeletedAssets.Dispose(); - } -#if UNITY_EDITOR - internal string FormatSummary() - { - var sb = new StringBuilder(); - FormatSummary(sb); - return sb.ToString(); - } - - internal void FormatSummary(StringBuilder sb) - { - sb.AppendLine(nameof(IncrementalConversionBatch)); - PrintOut(sb, nameof(DeletedInstanceIds), DeletedInstanceIds); - PrintOut(sb, nameof(ChangedInstanceIds), ChangedInstanceIds); - PrintOut(sb, nameof(ReconvertHierarchyInstanceIds), ReconvertHierarchyInstanceIds); - PrintOut(sb, nameof(ChangedAssets), ChangedAssets); - PrintOut(sb, nameof(DeletedAssets), DeletedAssets); - - if (ChangedComponents.Count > 0) - { - sb.Append(nameof(ChangedComponents)); - sb.Append(": "); - sb.Append(ChangedComponents.Count); - sb.AppendLine(); - foreach (var c in ChangedComponents) - { - sb.Append('\t'); - sb.Append(c.ToString()); - sb.AppendLine(); - } - sb.AppendLine(); - } - - if (!ParentChangeInstanceIds.IsEmpty) - { - sb.Append(nameof(ParentChangeInstanceIds)); - sb.Append(": "); - sb.Append(ParentChangeInstanceIds.Count()); - sb.AppendLine(); - foreach (var kvp in ParentChangeInstanceIds) - { - sb.Append('\t'); - sb.Append(kvp.Key); - sb.Append(" ("); - { - var obj = EditorUtility.InstanceIDToObject(kvp.Key); - if (obj == null) - sb.Append("null"); - else - sb.Append(obj.name); - } - sb.Append(") reparented to "); - sb.Append(kvp.Value); - sb.Append(" ("); - { - var obj = EditorUtility.InstanceIDToObject(kvp.Value); - if (obj == null) - sb.Append("null"); - else - sb.Append(obj.name); - } - sb.AppendLine(")"); - } - } - } - - static void PrintOut(StringBuilder sb, string name, NativeArray instanceIds) - { - if (instanceIds.Length == 0) - return; - sb.Append(name); - sb.Append(": "); - sb.Append(instanceIds.Length); - sb.AppendLine(); - for (int i = 0; i < instanceIds.Length; i++) - { - sb.Append('\t'); - sb.Append(instanceIds[i]); - sb.Append(" - "); - var obj = EditorUtility.InstanceIDToObject(instanceIds[i]); - if (obj == null) - sb.AppendLine("(null)"); - else - sb.AppendLine(obj.name); - } - - sb.AppendLine(); - } - - internal void EnsureFullyInitialized() - { - if (!DeletedInstanceIds.IsCreated) - DeletedInstanceIds = new NativeArray(0, Allocator.TempJob); - if (!ChangedInstanceIds.IsCreated) - ChangedInstanceIds = new NativeArray(0, Allocator.TempJob); - if (!ReconvertHierarchyInstanceIds.IsCreated) - ReconvertHierarchyInstanceIds = new NativeArray(0, Allocator.TempJob); - if (!ParentChangeInstanceIds.IsCreated) - ParentChangeInstanceIds = new NativeParallelHashMap(0, Allocator.TempJob); - if (!ChangedAssets.IsCreated) - ChangedAssets = new NativeArray(0, Allocator.TempJob); - if (!DeletedAssets.IsCreated) - DeletedAssets = new NativeArray(0, Allocator.TempJob); - if (ChangedComponents == null) - ChangedComponents = new List(); - } -#endif - } -} diff --git a/Unity.Entities.Hybrid/GameObjectConversion/Incremental/IncrementalConversionChangeTracker.cs.meta b/Unity.Entities.Hybrid/GameObjectConversion/Incremental/IncrementalConversionChangeTracker.cs.meta deleted file mode 100644 index 835f843..0000000 --- a/Unity.Entities.Hybrid/GameObjectConversion/Incremental/IncrementalConversionChangeTracker.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 5a4eef3addbd4036acda7ee1a204515c -timeCreated: 1587998099 \ No newline at end of file diff --git a/Unity.Entities.Hybrid/GameObjectConversion/MultiList.cs b/Unity.Entities.Hybrid/GameObjectConversion/MultiList.cs deleted file mode 100644 index 8041271..0000000 --- a/Unity.Entities.Hybrid/GameObjectConversion/MultiList.cs +++ /dev/null @@ -1,468 +0,0 @@ -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.Contracts; -using System.Linq; -using Unity.Collections; - -namespace Unity.Entities.Conversion -{ - static class MultiList - { - const int k_FirstUsageSize = 128; // first alloc will do this many elements - - public static int CalcEnsureCapacity(int current, int needed) - { - if (current == 0) - current = k_FirstUsageSize; - - while (current < needed) - current += current / 2; - - return current; - } - - public static bool CalcExpandCapacity(int current, ref int needed) - { - if (current >= needed) - return false; - - needed = CalcEnsureCapacity(current, needed); - return true; - } - } - - 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 - // * reuse of dealloc'd nodes: Next points to next free node - // -1 marks the end of a free/sublist - - // `HeadIds` is a front-end index to align a set of MultiLists on a key index, while supporting - // different sized sublists across MultiLists. - - public NativeArray HeadIds; - public NativeArray Next; - public int NextFree; - public I Data; - - public void Init() - { - NextFree = -1; - 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 - public void AddHead(int headIdIndex, in T data) - { - #if ENABLE_UNITY_COLLECTIONS_CHECKS - if (HeadIds[headIdIndex] >= 0) - throw new ArgumentException("Already a head at this index", nameof(headIdIndex)); - #endif - - var newId = Alloc(); - HeadIds[headIdIndex] = newId; - Next[newId] = -1; - Data.Set(newId, in data); - } - - // either add a head or insert at front (not tail!) of an existing list (returns id) - public void Add(int headIdIndex, in T data) - { - var newId = Alloc(); - - var headId = HeadIds[headIdIndex]; - if (headId < 0) - { - HeadIds[headIdIndex] = newId; - Next[newId] = -1; - } - else - { - Next[newId] = Next[headId]; - Next[headId] = newId; - } - Data.Set(newId, in data); - } - - public (int id, int serial) AddTail(int headIdIndex) - { - unsafe - { - int id; - int serial = AddTail(headIdIndex, &id, 1); - return (id, serial); - } - } - - // walk to end of the given list, add new entries and return the index of the first node added in the sublist. - public unsafe int AddTail(int headIdIndex, int* outIds, int count) - { - var headId = HeadIds[headIdIndex]; - int currentId = headId; - int serial = 1; - { - int next = Next[currentId]; - while (next > 0) - { - currentId = next; - next = Next[currentId]; - serial++; - } - } - - Alloc(outIds, count); - for (int i = 0; i < count; i++) - { - Next[currentId] = outIds[i]; - currentId = outIds[i]; - } - Next[currentId] = -1; - return serial; - } - - // walk to end of the given list, add new entry and return (id = node id within multilist, serial = node # within sublist) - public (int id, int serial) AddTail(int headIdIndex, in T data) - { - var added = AddTail(headIdIndex); - Data.Set(added.id, in data); - return added; - } - - // release an entire sublist, returning # items released - public int ReleaseList(int headIdIndex) - { - var headId = HeadIds[headIdIndex]; - HeadIds[headIdIndex] = -1; - - return ReleaseSubList(headId); - } - - // release a partial sublist (not the head), returning # items released - public int ReleaseListKeepHead(int headIdIndex) - { - var headId = HeadIds[headIdIndex]; - var startId = Next[headId]; - Next[headId] = -1; - - return ReleaseSubList(startId); - } - - int ReleaseSubList(int id) - { - var count = 0; - while (id >= 0) - { - ++count; - var next = Next[id]; - Release(id); - id = next; - } - return count; - } - - void Release(int id) - { - Next[id] = NextFree; - NextFree = id; - } - - [Pure] - public MultiListEnumerator SelectListAt(int headId) => - new MultiListEnumerator(Data, Next, headId); - - [Pure] - public MultiListEnumerator SelectList(int headIdIndex) => - new MultiListEnumerator(Data, Next, HeadIds[headIdIndex]); - - public bool TrySelectList(int headIdIndex, out MultiListEnumerator iter) - { - var headId = HeadIds[headIdIndex]; - if (headId < 0) - { - iter = MultiListEnumerator.Empty; - return false; - } - - iter = SelectListAt(headId); - return true; - } - - public void EnsureCapacity(int capacity) - { - if (Next.Length < 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; - Resize(ref HeadIds, newCapacity); - - for (var i = oldCapacity; i < newCapacity; ++i) - HeadIds[i] = -1; - } - - int Alloc() - { - if (NextFree < 0) - SetCapacity(MultiList.CalcEnsureCapacity(Next.Length, Next.Length + 1)); - - var newId = NextFree; - NextFree = Next[newId]; - - return newId; - } - - unsafe void Alloc(int* outIds, int count) - { - if (count == 0) - return; - if (NextFree < 0 || count > 1) - EnsureCapacity(MultiList.CalcEnsureCapacity(Next.Length, Next.Length + count)); - - int next = NextFree; - for (int i = 0; i < count; i++) - { - var newId = next; - outIds[i] = newId; - next = Next[newId]; - } - - NextFree = next; - } - - void SetCapacity(int newCapacity) - { - var oldCapacity = Next.Length; - - Resize(ref Next, newCapacity); - Data.Resize(newCapacity); - - for (var i = oldCapacity; i < newCapacity; ++i) - Next[i] = i + 1; - - Next[newCapacity - 1] = -1; - NextFree = oldCapacity; - } - } - - class MultiListEnumeratorDebugView where T : unmanaged - { - MultiListEnumerator m_Enumerator; - - public MultiListEnumeratorDebugView(MultiListEnumerator enumerator) - { - m_Enumerator = enumerator; - } - - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public T[] Items => m_Enumerator.ToArray(); - } - - struct MultiListEnumerator : IEnumerable, IEnumerator where I : IMultiListDataImpl - { - I m_Data; - NativeArray m_Next; - int m_StartIndex; - int m_CurIndex; - bool m_IsFirst; - - internal MultiListEnumerator(I data, NativeArray next, int startIndex) - { - m_Data = data; - m_Next = next; - m_StartIndex = startIndex; - m_CurIndex = -1; - m_IsFirst = true; - } - - public void Dispose() {} - - public static MultiListEnumerator Empty => new MultiListEnumerator(default, default, -1); - - IEnumerator IEnumerable.GetEnumerator() => this; - IEnumerator IEnumerable.GetEnumerator() => this; - - public bool MoveNext() - { - if (m_IsFirst) - { - m_CurIndex = m_StartIndex; - m_IsFirst = false; - } - else - m_CurIndex = m_Next[m_CurIndex]; - - return IsValid; - } - - public void Reset() - { - m_CurIndex = -1; - m_IsFirst = true; - } - - public T Current => m_Data.Get(m_CurIndex); - object IEnumerator.Current => Current; - - public bool IsEmpty => m_StartIndex < 0; - public bool Any => !IsEmpty; - public bool IsValid => m_CurIndex >= 0; - - public int Count() - { - var count = 0; - for (var i = m_StartIndex; i != -1; i = m_Next[i]) - ++count; - - return count; - } - } - - [DebuggerTypeProxy(typeof(MultiListEnumeratorDebugView<>))] - internal 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) where I : IMultiListDataImpl - { - var freeList = new List(); - for (var i = multiList.NextFree; i >= 0; i = multiList.Next[i]) - freeList.Add(i); - - var allLists = SelectAllLists(multiList.HeadIds, multiList.Next); - var enumerated = allLists.SelectMany(_ => _).Concat(freeList).ToList(); - - if (enumerated.Distinct().Count() != enumerated.Count) - throw new InvalidOperationException(); - } - - public static IEnumerable> SelectAllLists(NativeArray headIds, NativeArray next) - { - foreach (var headId in headIds) - { - if (headId >= 0) - { - var list = new List(); - - for (var i = headId; i >= 0; i = next[i]) - list.Add(i); - - yield return list; - } - } - } - - 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.Get(i))); - } - } -} diff --git a/Unity.Entities.Hybrid/GameObjectConversion/MultiList.cs.meta b/Unity.Entities.Hybrid/GameObjectConversion/MultiList.cs.meta deleted file mode 100644 index fcc7b7a..0000000 --- a/Unity.Entities.Hybrid/GameObjectConversion/MultiList.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 8c20ad845918e1e42a22fde57cf579e8 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Hybrid/GameObjectConversion/UnityEngineExtensions.cs b/Unity.Entities.Hybrid/GameObjectConversion/UnityEngineExtensions.cs index 5e7b608..1529f0a 100644 --- a/Unity.Entities.Hybrid/GameObjectConversion/UnityEngineExtensions.cs +++ b/Unity.Entities.Hybrid/GameObjectConversion/UnityEngineExtensions.cs @@ -9,22 +9,6 @@ namespace Unity.Entities.Conversion { static class UnityEngineExtensions { - /// - /// Obsolete. Returns a hash that can be used as a guid within a (non-persistent) session to refer to this UnityEngine.Object. - /// - [Obsolete("This function is no longer supported. (RemovedAfter 2021-03-01)")] - public static Hash128 ComputeInstanceHash(this UnityObject @this) - { - if (@this is Component component) - @this = component.gameObject; - - var instanceID = @this.GetInstanceID(); - - var hash = new UnityEngine.Hash128(); - HashUtilities.ComputeHash128(ref instanceID, ref hash); - return hash; - } - /// /// Returns an EntityGuid that can be used as a guid within a (non-persistent) session to refer to an entity generated /// from a UnityEngine.Object. The primary entity will be index 0, and additional entities will have increasing @@ -37,9 +21,6 @@ public static EntityGuid ComputeEntityGuid(this UnityObject @this, uint namespac return new EntityGuid(@this.GetInstanceID(), 0, namespaceId, (uint)serial); } - public static bool IsPrefab(this UnityObject @this) => - @this is GameObject go && IsPrefab(go); - public static bool IsPrefab(this GameObject @this) => !@this.scene.IsValid(); @@ -63,16 +44,6 @@ public static bool IsActiveIgnorePrefab(this GameObject @this) return true; } - [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] - public static void CheckObjectIsNotComponent(this UnityObject @this) - { - if (@this is Component) - { - // should not be possible to get here (user-callable API's will always auto request the Component's GameObject, unless we have a bug) - throw new InvalidOperationException(); - } - } - public static bool IsComponentDisabled(this Component @this) { switch (@this) @@ -105,48 +76,6 @@ public static bool GetComponentsBaking(this GameObject gameObject, List componentsCache) - { - int outputIndex = 0; - @this.GetComponents(componentsCache); - - if (maxComponentTypes < componentsCache.Count) - { - LogWarning($"Too many components on {@this.name}", @this); - return false; - } - - for (var i = 0; i != componentsCache.Count; i++) - { - var component = componentsCache[i]; - - if (component == null) - LogWarning($"The referenced script is missing on {@this.name} (index {i} in components list)", @this); - else if (!component.IsComponentDisabled()) - { - var componentType = component.GetType(); - var isUniqueType = true; - for (var j = 0; j < outputIndex; ++j) - { - if (componentTypes[j].Equals(componentType)) - { - isUniqueType = false; - break; - } - } - if (!isUniqueType) - continue; - - componentsCache[outputIndex] = component; - componentTypes[outputIndex] = componentType; - - outputIndex++; - } - } - componentsCache.RemoveRange(outputIndex, componentsCache.Count - outputIndex); return true; } diff --git a/Unity.Entities.Hybrid/JetBrains.Annotations.cs b/Unity.Entities.Hybrid/JetBrains.Annotations.cs deleted file mode 100644 index 0238bee..0000000 --- a/Unity.Entities.Hybrid/JetBrains.Annotations.cs +++ /dev/null @@ -1,1061 +0,0 @@ -#if UNITY_DOTSRUNTIME -/* MIT License - -Copyright (c) 2016 JetBrains http://www.jetbrains.com - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. */ - -using System; - -#pragma warning disable 1591 -// ReSharper disable UnusedMember.Global -// ReSharper disable MemberCanBePrivate.Global -// ReSharper disable UnusedAutoPropertyAccessor.Global -// ReSharper disable IntroduceOptionalParameters.Global -// ReSharper disable MemberCanBeProtected.Global -// ReSharper disable InconsistentNaming - -namespace JetBrains.Annotations -{ - /// - /// Indicates that the value of the marked element could be null sometimes, - /// so the check for null is necessary before its usage. - /// - /// - /// [CanBeNull] object Test() => null; - /// - /// void UseTest() { - /// var p = Test(); - /// var s = p.ToString(); // Warning: Possible 'System.NullReferenceException' - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] - internal sealed class CanBeNullAttribute : Attribute {} - - /// - /// Indicates that the value of the marked element could never be null. - /// - /// - /// [NotNull] object Foo() { - /// return null; // Warning: Possible 'null' assignment - /// } - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field | AttributeTargets.Event | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.GenericParameter)] - internal sealed class NotNullAttribute : Attribute {} - - /// - /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task - /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property - /// or of the Lazy.Value property can never be null. - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field)] - internal sealed class ItemNotNullAttribute : Attribute {} - - /// - /// Can be appplied to symbols of types derived from IEnumerable as well as to symbols of Task - /// and Lazy classes to indicate that the value of a collection item, of the Task.Result property - /// or of the Lazy.Value property can be null. - /// - [AttributeUsage( - AttributeTargets.Method | AttributeTargets.Parameter | AttributeTargets.Property | - AttributeTargets.Delegate | AttributeTargets.Field)] - internal sealed class ItemCanBeNullAttribute : Attribute {} - - /// - /// Indicates that the marked method builds string by format pattern and (optional) arguments. - /// Parameter, which contains format string, should be given in constructor. The format string - /// should be in -like form. - /// - /// - /// [StringFormatMethod("message")] - /// void ShowError(string message, params object[] args) { /* do something */ } - /// - /// void Foo() { - /// ShowError("Failed: {0}"); // Warning: Non-existing argument in format string - /// } - /// - [AttributeUsage( - AttributeTargets.Constructor | AttributeTargets.Method | - AttributeTargets.Property | AttributeTargets.Delegate)] - internal sealed class StringFormatMethodAttribute : Attribute - { - /// - /// Specifies which parameter of an annotated method should be treated as format-string - /// - public StringFormatMethodAttribute([NotNull] string formatParameterName) - { - FormatParameterName = formatParameterName; - } - - [NotNull] public string FormatParameterName { get; private set; } - } - - /// - /// For a parameter that is expected to be one of the limited set of values. - /// Specify fields of which type should be used as values for this parameter. - /// - [AttributeUsage( - AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field, - AllowMultiple = true)] - internal sealed class ValueProviderAttribute : Attribute - { - public ValueProviderAttribute([NotNull] string name) - { - Name = name; - } - - [NotNull] public string Name { get; private set; } - } - - /// - /// Indicates that the function argument should be string literal and match one - /// of the parameters of the caller function. For example, ReSharper annotates - /// the parameter of . - /// - /// - /// void Foo(string param) { - /// if (param == null) - /// throw new ArgumentNullException("par"); // Warning: Cannot resolve symbol - /// } - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class InvokerParameterNameAttribute : Attribute {} - - /// - /// Indicates that the method is contained in a type that implements - /// System.ComponentModel.INotifyPropertyChanged interface and this method - /// is used to notify that some property value changed. - /// - /// - /// The method should be non-static and conform to one of the supported signatures: - /// - /// NotifyChanged(string) - /// NotifyChanged(params string[]) - /// NotifyChanged{T}(Expression{Func{T}}) - /// NotifyChanged{T,U}(Expression{Func{T,U}}) - /// SetProperty{T}(ref T, T, string) - /// - /// - /// - /// public class Foo : INotifyPropertyChanged { - /// public event PropertyChangedEventHandler PropertyChanged; - /// - /// [NotifyPropertyChangedInvocator] - /// protected virtual void NotifyChanged(string propertyName) { ... } - /// - /// string _name; - /// - /// public string Name { - /// get { return _name; } - /// set { _name = value; NotifyChanged("LastName"); /* Warning */ } - /// } - /// } - /// - /// Examples of generated notifications: - /// - /// NotifyChanged("Property") - /// NotifyChanged(() => Property) - /// NotifyChanged((VM x) => x.Property) - /// SetProperty(ref myField, value, "Property") - /// - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class NotifyPropertyChangedInvocatorAttribute : Attribute - { - public NotifyPropertyChangedInvocatorAttribute() {} - public NotifyPropertyChangedInvocatorAttribute([NotNull] string parameterName) - { - ParameterName = parameterName; - } - - [CanBeNull] public string ParameterName { get; private set; } - } - - /// - /// Describes dependency between method input and output. - /// - /// - ///

Function Definition Table syntax:

- /// - /// FDT ::= FDTRow [;FDTRow]* - /// FDTRow ::= Input => Output | Output <= Input - /// Input ::= ParameterName: Value [, Input]* - /// Output ::= [ParameterName: Value]* {halt|stop|void|nothing|Value} - /// Value ::= true | false | null | notnull | canbenull - /// - /// If method has single input parameter, it's name could be omitted.
- /// Using halt (or void/nothing, which is the same) for method output - /// means that the methos doesn't return normally (throws or terminates the process).
- /// Value canbenull is only applicable for output parameters.
- /// You can use multiple [ContractAnnotation] for each FDT row, or use single attribute - /// with rows separated by semicolon. There is no notion of order rows, all rows are checked - /// for applicability and applied per each program state tracked by R# analysis.
- ///
- /// - /// - /// [ContractAnnotation("=> halt")] - /// public void TerminationMethod() - /// - /// - /// [ContractAnnotation("halt <= condition: false")] - /// public void Assert(bool condition, string text) // regular assertion method - /// - /// - /// [ContractAnnotation("s:null => true")] - /// public bool IsNullOrEmpty(string s) // string.IsNullOrEmpty() - /// - /// - /// // A method that returns null if the parameter is null, - /// // and not null if the parameter is not null - /// [ContractAnnotation("null => null; notnull => notnull")] - /// public object Transform(object data) - /// - /// - /// [ContractAnnotation("=> true, result: notnull; => false, result: null")] - /// public bool TryParse(string s, out Person result) - /// - /// - [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)] - internal sealed class ContractAnnotationAttribute : Attribute - { - public ContractAnnotationAttribute([NotNull] string contract) - : this(contract, false) {} - - public ContractAnnotationAttribute([NotNull] string contract, bool forceFullStates) - { - Contract = contract; - ForceFullStates = forceFullStates; - } - - [NotNull] public string Contract { get; private set; } - - public bool ForceFullStates { get; private set; } - } - - /// - /// Indicates that marked element should be localized or not. - /// - /// - /// [LocalizationRequiredAttribute(true)] - /// class Foo { - /// string str = "my string"; // Warning: Localizable string - /// } - /// - [AttributeUsage(AttributeTargets.All)] - internal sealed class LocalizationRequiredAttribute : Attribute - { - public LocalizationRequiredAttribute() : this(true) {} - - public LocalizationRequiredAttribute(bool required) - { - Required = required; - } - - public bool Required { get; private set; } - } - - /// - /// Indicates that the value of the marked type (or its derivatives) - /// cannot be compared using '==' or '!=' operators and Equals() - /// should be used instead. However, using '==' or '!=' for comparison - /// with null is always permitted. - /// - /// - /// [CannotApplyEqualityOperator] - /// class NoEquality { } - /// - /// class UsesNoEquality { - /// void Test() { - /// var ca1 = new NoEquality(); - /// var ca2 = new NoEquality(); - /// if (ca1 != null) { // OK - /// bool condition = ca1 == ca2; // Warning - /// } - /// } - /// } - /// - [AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class | AttributeTargets.Struct)] - internal sealed class CannotApplyEqualityOperatorAttribute : Attribute {} - - /// - /// When applied to a target attribute, specifies a requirement for any type marked - /// with the target attribute to implement or inherit specific type or types. - /// - /// - /// [BaseTypeRequired(typeof(IComponent)] // Specify requirement - /// class ComponentAttribute : Attribute { } - /// - /// [Component] // ComponentAttribute requires implementing IComponent interface - /// class MyComponent : IComponent { } - /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - [BaseTypeRequired(typeof(Attribute))] - internal sealed class BaseTypeRequiredAttribute : Attribute - { - public BaseTypeRequiredAttribute([NotNull] Type baseType) - { - BaseType = baseType; - } - - [NotNull] public Type BaseType { get; private set; } - } - - /// - /// Indicates that the marked symbol is used implicitly (e.g. via reflection, in external library), - /// so this symbol will not be marked as unused (as well as by other usage inspections). - /// - [AttributeUsage(AttributeTargets.All)] - internal sealed class UsedImplicitlyAttribute : Attribute - { - public UsedImplicitlyAttribute() - : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) {} - - public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags) - : this(useKindFlags, ImplicitUseTargetFlags.Default) {} - - public UsedImplicitlyAttribute(ImplicitUseTargetFlags targetFlags) - : this(ImplicitUseKindFlags.Default, targetFlags) {} - - public UsedImplicitlyAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) - { - UseKindFlags = useKindFlags; - TargetFlags = targetFlags; - } - - public ImplicitUseKindFlags UseKindFlags { get; private set; } - - public ImplicitUseTargetFlags TargetFlags { get; private set; } - } - - /// - /// Should be used on attributes and causes ReSharper to not mark symbols marked with such attributes - /// as unused (as well as by other usage inspections) - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.GenericParameter)] - internal sealed class MeansImplicitUseAttribute : Attribute - { - public MeansImplicitUseAttribute() - : this(ImplicitUseKindFlags.Default, ImplicitUseTargetFlags.Default) {} - - public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags) - : this(useKindFlags, ImplicitUseTargetFlags.Default) {} - - public MeansImplicitUseAttribute(ImplicitUseTargetFlags targetFlags) - : this(ImplicitUseKindFlags.Default, targetFlags) {} - - public MeansImplicitUseAttribute(ImplicitUseKindFlags useKindFlags, ImplicitUseTargetFlags targetFlags) - { - UseKindFlags = useKindFlags; - TargetFlags = targetFlags; - } - - [UsedImplicitly] public ImplicitUseKindFlags UseKindFlags { get; private set; } - - [UsedImplicitly] public ImplicitUseTargetFlags TargetFlags { get; private set; } - } - - [Flags] - internal enum ImplicitUseKindFlags - { - Default = Access | Assign | InstantiatedWithFixedConstructorSignature, - /// Only entity marked with attribute considered used. - Access = 1, - /// Indicates implicit assignment to a member. - Assign = 2, - /// - /// Indicates implicit instantiation of a type with fixed constructor signature. - /// That means any unused constructor parameters won't be reported as such. - /// - InstantiatedWithFixedConstructorSignature = 4, - /// Indicates implicit instantiation of a type. - InstantiatedNoFixedConstructorSignature = 8, - } - - /// - /// Specify what is considered used implicitly when marked - /// with or . - /// - [Flags] - internal enum ImplicitUseTargetFlags - { - Default = Itself, - Itself = 1, - /// Members of entity marked with attribute are considered used. - Members = 2, - /// Entity marked with attribute and all its members considered used. - WithMembers = Itself | Members - } - - /// - /// This attribute is intended to mark publicly available API - /// which should not be removed and so is treated as used. - /// - [MeansImplicitUse(ImplicitUseTargetFlags.WithMembers)] - internal sealed class PublicAPIAttribute : Attribute - { - public PublicAPIAttribute() {} - - public PublicAPIAttribute([NotNull] string comment) - { - Comment = comment; - } - - [CanBeNull] public string Comment { get; private set; } - } - - /// - /// Tells code analysis engine if the parameter is completely handled when the invoked method is on stack. - /// If the parameter is a delegate, indicates that delegate is executed while the method is executed. - /// If the parameter is an enumerable, indicates that it is enumerated while the method is executed. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class InstantHandleAttribute : Attribute {} - - /// - /// Indicates that a method does not make any observable state changes. - /// The same as System.Diagnostics.Contracts.PureAttribute. - /// - /// - /// [Pure] int Multiply(int x, int y) => x * y; - /// - /// void M() { - /// Multiply(123, 42); // Waring: Return value of pure method is not used - /// } - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class PureAttribute : Attribute {} - - /// - /// Indicates that the return value of method invocation must be used. - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class MustUseReturnValueAttribute : Attribute - { - public MustUseReturnValueAttribute() {} - - public MustUseReturnValueAttribute([NotNull] string justification) - { - Justification = justification; - } - - [CanBeNull] public string Justification { get; private set; } - } - - /// - /// Indicates the type member or parameter of some type, that should be used instead of all other ways - /// to get the value that type. This annotation is useful when you have some "context" value evaluated - /// and stored somewhere, meaning that all other ways to get this value must be consolidated with existing one. - /// - /// - /// class Foo { - /// [ProvidesContext] IBarService _barService = ...; - /// - /// void ProcessNode(INode node) { - /// DoSomething(node, node.GetGlobalServices().Bar); - /// // ^ Warning: use value of '_barService' field - /// } - /// } - /// - [AttributeUsage( - AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Parameter | AttributeTargets.Method | - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.GenericParameter)] - internal sealed class ProvidesContextAttribute : Attribute {} - - /// - /// Indicates that a parameter is a path to a file or a folder within a web project. - /// Path can be relative or absolute, starting from web root (~). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class PathReferenceAttribute : Attribute - { - public PathReferenceAttribute() {} - - public PathReferenceAttribute([NotNull, PathReference] string basePath) - { - BasePath = basePath; - } - - [CanBeNull] public string BasePath { get; private set; } - } - - /// - /// An extension method marked with this attribute is processed by ReSharper code completion - /// as a 'Source Template'. When extension method is completed over some expression, it's source code - /// is automatically expanded like a template at call site. - /// - /// - /// Template method body can contain valid source code and/or special comments starting with '$'. - /// Text inside these comments is added as source code when the template is applied. Template parameters - /// can be used either as additional method parameters or as identifiers wrapped in two '$' signs. - /// Use the attribute to specify macros for parameters. - /// - /// - /// In this example, the 'forEach' method is a source template available over all values - /// of enumerable types, producing ordinary C# 'foreach' statement and placing caret inside block: - /// - /// [SourceTemplate] - /// public static void forEach<T>(this IEnumerable<T> xs) { - /// foreach (var x in xs) { - /// //$ $END$ - /// } - /// } - /// - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class SourceTemplateAttribute : Attribute {} - - /// - /// Allows specifying a macro for a parameter of a source template. - /// - /// - /// You can apply the attribute on the whole method or on any of its additional parameters. The macro expression - /// is defined in the property. When applied on a method, the target - /// template parameter is defined in the property. To apply the macro silently - /// for the parameter, set the property value = -1. - /// - /// - /// Applying the attribute on a source template method: - /// - /// [SourceTemplate, Macro(Target = "item", Expression = "suggestVariableName()")] - /// public static void forEach<T>(this IEnumerable<T> collection) { - /// foreach (var item in collection) { - /// //$ $END$ - /// } - /// } - /// - /// Applying the attribute on a template method parameter: - /// - /// [SourceTemplate] - /// public static void something(this Entity x, [Macro(Expression = "guid()", Editable = -1)] string newguid) { - /// /*$ var $x$Id = "$newguid$" + x.ToString(); - /// x.DoSomething($x$Id); */ - /// } - /// - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method, AllowMultiple = true)] - internal sealed class MacroAttribute : Attribute - { - /// - /// Allows specifying a macro that will be executed for a source template - /// parameter when the template is expanded. - /// - [CanBeNull] public string Expression { get; set; } - - /// - /// Allows specifying which occurrence of the target parameter becomes editable when the template is deployed. - /// - /// - /// If the target parameter is used several times in the template, only one occurrence becomes editable; - /// other occurrences are changed synchronously. To specify the zero-based index of the editable occurrence, - /// use values >= 0. To make the parameter non-editable when the template is expanded, use -1. - /// > - public int Editable { get; set; } - - /// - /// Identifies the target parameter of a source template if the - /// is applied on a template method. - /// - [CanBeNull] public string Target { get; set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcAreaMasterLocationFormatAttribute : Attribute - { - public AspMvcAreaMasterLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcAreaPartialViewLocationFormatAttribute : Attribute - { - public AspMvcAreaPartialViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcAreaViewLocationFormatAttribute : Attribute - { - public AspMvcAreaViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcMasterLocationFormatAttribute : Attribute - { - public AspMvcMasterLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcPartialViewLocationFormatAttribute : Attribute - { - public AspMvcPartialViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = true)] - internal sealed class AspMvcViewLocationFormatAttribute : Attribute - { - public AspMvcViewLocationFormatAttribute([NotNull] string format) - { - Format = format; - } - - [NotNull] public string Format { get; private set; } - } - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC action. If applied to a method, the MVC action name is calculated - /// implicitly from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class AspMvcActionAttribute : Attribute - { - public AspMvcActionAttribute() {} - - public AspMvcActionAttribute([NotNull] string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } - - [CanBeNull] public string AnonymousProperty { get; private set; } - } - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC area. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcAreaAttribute : Attribute - { - public AspMvcAreaAttribute() {} - - public AspMvcAreaAttribute([NotNull] string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } - - [CanBeNull] public string AnonymousProperty { get; private set; } - } - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is - /// an MVC controller. If applied to a method, the MVC controller name is calculated - /// implicitly from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.ChildActionExtensions.RenderAction(HtmlHelper, String, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class AspMvcControllerAttribute : Attribute - { - public AspMvcControllerAttribute() {} - - public AspMvcControllerAttribute([NotNull] string anonymousProperty) - { - AnonymousProperty = anonymousProperty; - } - - [CanBeNull] public string AnonymousProperty { get; private set; } - } - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC Master. Use this attribute - /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, String). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcMasterAttribute : Attribute {} - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC model type. Use this attribute - /// for custom wrappers similar to System.Web.Mvc.Controller.View(String, Object). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcModelTypeAttribute : Attribute {} - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter is an MVC - /// partial view. If applied to a method, the MVC partial view name is calculated implicitly - /// from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.RenderPartialExtensions.RenderPartial(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class AspMvcPartialViewAttribute : Attribute {} - - /// - /// ASP.NET MVC attribute. Allows disabling inspections for MVC views within a class or a method. - /// - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] - internal sealed class AspMvcSuppressViewErrorAttribute : Attribute {} - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC display template. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.DisplayExtensions.DisplayForModel(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcDisplayTemplateAttribute : Attribute {} - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC editor template. - /// Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Html.EditorExtensions.EditorForModel(HtmlHelper, String). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcEditorTemplateAttribute : Attribute {} - - /// - /// ASP.NET MVC attribute. Indicates that a parameter is an MVC template. - /// Use this attribute for custom wrappers similar to - /// System.ComponentModel.DataAnnotations.UIHintAttribute(System.String). - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcTemplateAttribute : Attribute {} - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component. If applied to a method, the MVC view name is calculated implicitly - /// from the context. Use this attribute for custom wrappers similar to - /// System.Web.Mvc.Controller.View(Object). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class AspMvcViewAttribute : Attribute {} - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component name. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AspMvcViewComponentAttribute : Attribute {} - - /// - /// ASP.NET MVC attribute. If applied to a parameter, indicates that the parameter - /// is an MVC view component view. If applied to a method, the MVC view component view name is default. - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class AspMvcViewComponentViewAttribute : Attribute {} - - /// - /// ASP.NET MVC attribute. When applied to a parameter of an attribute, - /// indicates that this parameter is an MVC action name. - /// - /// - /// [ActionName("Foo")] - /// public ActionResult Login(string returnUrl) { - /// ViewBag.ReturnUrl = Url.Action("Foo"); // OK - /// return RedirectToAction("Bar"); // Error: Cannot resolve action - /// } - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property)] - internal sealed class AspMvcActionSelectorAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.Field)] - internal sealed class HtmlElementAttributesAttribute : Attribute - { - public HtmlElementAttributesAttribute() {} - - public HtmlElementAttributesAttribute([NotNull] string name) - { - Name = name; - } - - [CanBeNull] public string Name { get; private set; } - } - - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Field | AttributeTargets.Property)] - internal sealed class HtmlAttributeValueAttribute : Attribute - { - public HtmlAttributeValueAttribute([NotNull] string name) - { - Name = name; - } - - [NotNull] public string Name { get; private set; } - } - - /// - /// Razor attribute. Indicates that a parameter or a method is a Razor section. - /// Use this attribute for custom wrappers similar to - /// System.Web.WebPages.WebPageBase.RenderSection(String). - /// - [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Method)] - internal sealed class RazorSectionAttribute : Attribute {} - - /// - /// Indicates how method, constructor invocation or property access - /// over collection type affects content of the collection. - /// - [AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Property)] - internal sealed class CollectionAccessAttribute : Attribute - { - public CollectionAccessAttribute(CollectionAccessType collectionAccessType) - { - CollectionAccessType = collectionAccessType; - } - - public CollectionAccessType CollectionAccessType { get; private set; } - } - - [Flags] - internal enum CollectionAccessType - { - /// Method does not use or modify content of the collection. - None = 0, - /// Method only reads content of the collection but does not modify it. - Read = 1, - /// Method can change content of the collection but does not add new elements. - ModifyExistingContent = 2, - /// Method can add new elements to the collection. - UpdatedContent = ModifyExistingContent | 4 - } - - /// - /// Indicates that the marked method is assertion method, i.e. it halts control flow if - /// one of the conditions is satisfied. To set the condition, mark one of the parameters with - /// attribute. - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class AssertionMethodAttribute : Attribute {} - - /// - /// Indicates the condition parameter of the assertion method. The method itself should be - /// marked by attribute. The mandatory argument of - /// the attribute is the assertion type. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class AssertionConditionAttribute : Attribute - { - public AssertionConditionAttribute(AssertionConditionType conditionType) - { - ConditionType = conditionType; - } - - public AssertionConditionType ConditionType { get; private set; } - } - - /// - /// Specifies assertion type. If the assertion method argument satisfies the condition, - /// then the execution continues. Otherwise, execution is assumed to be halted. - /// - internal enum AssertionConditionType - { - /// Marked parameter should be evaluated to true. - IS_TRUE = 0, - /// Marked parameter should be evaluated to false. - IS_FALSE = 1, - /// Marked parameter should be evaluated to null value. - IS_NULL = 2, - /// Marked parameter should be evaluated to not null value. - IS_NOT_NULL = 3, - } - - /// - /// Indicates that method is pure LINQ method, with postponed enumeration (like Enumerable.Select, - /// .Where). This annotation allows inference of [InstantHandle] annotation for parameters - /// of delegate type by analyzing LINQ method chains. - /// - [AttributeUsage(AttributeTargets.Method)] - internal sealed class LinqTunnelAttribute : Attribute {} - - /// - /// Indicates that IEnumerable, passed as parameter, is not enumerated. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class NoEnumerationAttribute : Attribute {} - - /// - /// Indicates that parameter is regular expression pattern. - /// - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class RegexPatternAttribute : Attribute {} - - /// - /// Prevents the Member Reordering feature from tossing members of the marked class. - /// - /// - /// The attribute must be mentioned in your member reordering patterns - /// - [AttributeUsage( - AttributeTargets.Class | AttributeTargets.Interface | AttributeTargets.Struct | AttributeTargets.Enum)] - internal sealed class NoReorderAttribute : Attribute {} - - /// - /// XAML attribute. Indicates the type that has ItemsSource property and should be treated - /// as ItemsControl-derived type, to enable inner items DataContext type resolve. - /// - [AttributeUsage(AttributeTargets.Class)] - internal sealed class XamlItemsControlAttribute : Attribute {} - - /// - /// XAML attribute. Indicates the property of some BindingBase-derived type, that - /// is used to bind some item of ItemsControl-derived type. This annotation will - /// enable the DataContext type resolve for XAML bindings for such properties. - /// - /// - /// Property should have the tree ancestor of the ItemsControl type or - /// marked with the attribute. - /// - [AttributeUsage(AttributeTargets.Property)] - internal sealed class XamlItemBindingOfItemsControlAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - internal sealed class AspChildControlTypeAttribute : Attribute - { - public AspChildControlTypeAttribute([NotNull] string tagName, [NotNull] Type controlType) - { - TagName = tagName; - ControlType = controlType; - } - - [NotNull] public string TagName { get; private set; } - - [NotNull] public Type ControlType { get; private set; } - } - - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] - internal sealed class AspDataFieldAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] - internal sealed class AspDataFieldsAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Property)] - internal sealed class AspMethodPropertyAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true)] - internal sealed class AspRequiredAttributeAttribute : Attribute - { - public AspRequiredAttributeAttribute([NotNull] string attribute) - { - Attribute = attribute; - } - - [NotNull] public string Attribute { get; private set; } - } - - [AttributeUsage(AttributeTargets.Property)] - internal sealed class AspTypePropertyAttribute : Attribute - { - public bool CreateConstructorReferences { get; private set; } - - public AspTypePropertyAttribute(bool createConstructorReferences) - { - CreateConstructorReferences = createConstructorReferences; - } - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorImportNamespaceAttribute : Attribute - { - public RazorImportNamespaceAttribute([NotNull] string name) - { - Name = name; - } - - [NotNull] public string Name { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorInjectionAttribute : Attribute - { - public RazorInjectionAttribute([NotNull] string type, [NotNull] string fieldName) - { - Type = type; - FieldName = fieldName; - } - - [NotNull] public string Type { get; private set; } - - [NotNull] public string FieldName { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorDirectiveAttribute : Attribute - { - public RazorDirectiveAttribute([NotNull] string directive) - { - Directive = directive; - } - - [NotNull] public string Directive { get; private set; } - } - - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - internal sealed class RazorPageBaseTypeAttribute : Attribute - { - public RazorPageBaseTypeAttribute([NotNull] string baseType) - { - BaseType = baseType; - } - - public RazorPageBaseTypeAttribute([NotNull] string baseType, string pageName) - { - BaseType = baseType; - PageName = pageName; - } - - [NotNull] public string BaseType { get; private set; } - [CanBeNull] public string PageName { get; private set; } - } - - [AttributeUsage(AttributeTargets.Method)] - internal sealed class RazorHelperCommonAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Property)] - internal sealed class RazorLayoutAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Method)] - internal sealed class RazorWriteLiteralMethodAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Method)] - internal sealed class RazorWriteMethodAttribute : Attribute {} - - [AttributeUsage(AttributeTargets.Parameter)] - internal sealed class RazorWriteMethodParameterAttribute : Attribute {} -} - -#endif diff --git a/Unity.Entities.Hybrid/JetBrains.Annotations.cs.meta b/Unity.Entities.Hybrid/JetBrains.Annotations.cs.meta deleted file mode 100644 index bceb4dd..0000000 --- a/Unity.Entities.Hybrid/JetBrains.Annotations.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 0b9ee62fe268b554bbe025127bf2ba23 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Hybrid/Unity.Entities.Hybrid.asmdef b/Unity.Entities.Hybrid/Unity.Entities.Hybrid.asmdef index 46637c0..6102469 100644 --- a/Unity.Entities.Hybrid/Unity.Entities.Hybrid.asmdef +++ b/Unity.Entities.Hybrid/Unity.Entities.Hybrid.asmdef @@ -22,20 +22,13 @@ "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [ - "!UNITY_DOTSRUNTIME" - ], + "defineConstraints": [], "versionDefines": [ { "name": "com.unity.package-validation-suite", "expression": "0.4.0-preview.6", "define": "UNITY_SKIP_UPDATES_WITH_VALIDATION_SUITE" }, - { - "name": "com.unity.platforms", - "expression": "", - "define": "USING_PLATFORMS_PACKAGE" - }, { "name": "Unity", "expression": "2022.2.14f1", diff --git a/Unity.Entities.PerformanceTests/ChunkStorePerformanceTests.cs b/Unity.Entities.PerformanceTests/ChunkStorePerformanceTests.cs index 6c5f6c2..68ec745 100644 --- a/Unity.Entities.PerformanceTests/ChunkStorePerformanceTests.cs +++ b/Unity.Entities.PerformanceTests/ChunkStorePerformanceTests.cs @@ -10,14 +10,13 @@ public class ChunkStorePerformanceTests { unsafe struct ChunkStoreTestHarness : IDisposable { - public EntityComponentStore.ChunkStore ChunkStore; - public NativeList Chunks; + public NativeList Chunks; Mathematics.Random Seed; readonly int ChunkCount; public ChunkStoreTestHarness(int chunkCount, uint seed) { - ChunkStore = default; - Chunks = new NativeList(chunkCount, Allocator.Persistent); + EntityComponentStore.PerChunkArray.Initialize(); + Chunks = new NativeList(chunkCount, Allocator.Persistent); Seed = new Mathematics.Random(seed); ChunkCount = chunkCount; for(var i = 0; i < ChunkCount; ++i) @@ -25,14 +24,14 @@ public ChunkStoreTestHarness(int chunkCount, uint seed) } public void AllocateOne() { - var error = ChunkStore.AllocateContiguousChunks(out Chunk* value, 1, out int _); + var error = EntityComponentStore.s_chunkStore.Data.AllocateContiguousChunks(out ChunkIndex value, 1, out int _); Assert.AreEqual(0, error); - Chunks.Add((IntPtr)value); + Chunks.Add(value); } public void FreeAtIndex(int index) { - Chunk* value = (Chunk*)Chunks[index]; - var error = ChunkStore.FreeContiguousChunks(value, 1); + var value = Chunks[index]; + var error = EntityComponentStore.s_chunkStore.Data.FreeContiguousChunks(value, 1); Assert.AreEqual(0, error); } public void FreeOne() @@ -52,21 +51,11 @@ public void Exercise() for(var i = 0; i < ChunkCount/2; ++i) AllocateOne(); } - public void Free() - { - for(var i = 0; i < Chunks.Length; ++i) - { - Chunk* value = (Chunk*)Chunks[i]; - var error = ChunkStore.FreeContiguousChunks(value, 1); - Assert.AreEqual(0, error); - } - } public void Dispose() { for(var i = 0; i < Chunks.Length; ++i) FreeAtIndex(i); Chunks.Dispose(); - ChunkStore.Dispose(); this = default; } } diff --git a/Unity.Entities.PerformanceTests/EntityManagerPerformanceTests.cs b/Unity.Entities.PerformanceTests/EntityManagerPerformanceTests.cs index 3e80f30..3e8b556 100644 --- a/Unity.Entities.PerformanceTests/EntityManagerPerformanceTests.cs +++ b/Unity.Entities.PerformanceTests/EntityManagerPerformanceTests.cs @@ -940,35 +940,34 @@ public void DestroyEntityArchetypeSame([Values(1, 10, 1000, 10000)] int size) .Run(); } - unsafe void CreateChunks(NativeArray chunks) + unsafe void CreateChunks(NativeArray chunks) { var chunkStore = (EntityComponentStore.ChunkStore*)UnsafeUtility.AddressOf(ref EntityComponentStore.s_chunkStore.Data); for(int i = 0; i < chunks.Length; ++i) { - chunkStore->AllocateContiguousChunks(out Chunk* chunk, 1, out int _); - chunks[i] = (IntPtr)chunk; + chunkStore->AllocateContiguousChunks(out ChunkIndex chunk, 1, out int _); + chunks[i] = chunk; } } - unsafe void DestroyChunks(NativeArray chunks) + unsafe void DestroyChunks(NativeArray chunks) { var chunkStore = (EntityComponentStore.ChunkStore*)UnsafeUtility.AddressOf(ref EntityComponentStore.s_chunkStore.Data); for(int i = chunks.Length; i --> 0;) { - Chunk* chunk = (Chunk*)chunks[i]; - chunkStore->FreeContiguousChunks(chunk, 1); + chunkStore->FreeContiguousChunks(chunks[i], 1); } } [Test, Performance] public void CreateChunks([Values(100000)] int size) { - var chunks = default(NativeArray); + var chunks = default(NativeArray); Measure.Method(() => { CreateChunks(chunks); }) - .SetUp(() => { chunks = new NativeArray(size, Allocator.Persistent); }) + .SetUp(() => { chunks = new NativeArray(size, Allocator.Persistent); }) .CleanUp(() => { DestroyChunks(chunks); chunks.Dispose(); }) .WarmupCount(1) .MeasurementCount(10) @@ -978,12 +977,12 @@ public void CreateChunks([Values(100000)] int size) [Test, Performance] public void DestroyChunks([Values(100000)] int size) { - var chunks = default(NativeArray); + var chunks = default(NativeArray); Measure.Method(() => { DestroyChunks(chunks); }) - .SetUp(() => { chunks = new NativeArray(size, Allocator.Persistent); CreateChunks(chunks); }) + .SetUp(() => { chunks = new NativeArray(size, Allocator.Persistent); CreateChunks(chunks); }) .CleanUp(() => { chunks.Dispose(); }) .WarmupCount(1) .MeasurementCount(10) diff --git a/Unity.Entities.PerformanceTests/JobEntityPerformanceTests.cs b/Unity.Entities.PerformanceTests/JobEntityPerformanceTests.cs index 42e922d..5683513 100644 --- a/Unity.Entities.PerformanceTests/JobEntityPerformanceTests.cs +++ b/Unity.Entities.PerformanceTests/JobEntityPerformanceTests.cs @@ -154,7 +154,6 @@ public void UpdateComponentValueTo10Job_Run() public JobHandle UpdateComponentValueTo10Job_ScheduleParallel(JobHandle jobHandle) { - var job = new EcsTestSetComponentValueTo10(); var newJobHandle = job.ScheduleParallel(jobHandle); return newJobHandle; @@ -167,54 +166,12 @@ public JobHandle UpdateComponentValueTo10Job_Schedule(JobHandle jobHandle) return newJobHandle; } - public JobHandle UpdateComponentValueTo10Job(JobHandle dependsOn = default) - { - var query = GetEntityQuery(new EntityQueryDesc - { - All = new ComponentType[] { typeof(EcsTestData), typeof(EcsTestData2) } - }); - var job = new EcsTestSetComponentValueTo10(); - var jobHandle = job.ScheduleParallel(query, dependsOn); - return jobHandle; - } - - public JobHandle UpdateOnlyComponentValueTo10Job_WithSharedComponentFilter_10(JobHandle dependsOn = default) + public JobHandle UpdateComponentValueTo10Job(EntityQuery query, JobHandle dependsOn) { - var query = GetEntityQuery(new EntityQueryDesc - { - All = new ComponentType[] { typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestSharedComp) } - }); - query.SetSharedComponentFilterManaged(new EcsTestSharedComp {value = 10}); var job = new EcsTestSetComponentValueTo10(); var jobHandle = job.ScheduleParallel(query, dependsOn); return jobHandle; } - - public JobHandle UpdateFirstComponentValueTo10Job_WithSharedComponentFilter_0(bool withEcsTestData2, JobHandle dependsOn = default) - { - if (withEcsTestData2) - { - var query = GetEntityQuery(new EntityQueryDesc - { - All = new ComponentType[] { typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestSharedComp) } - }); - query.SetSharedComponentFilterManaged(new EcsTestSharedComp {value = 10}); - var job = new EcsTestSetComponentValueTo10(); - var jobHandle = job.ScheduleParallel(query, dependsOn); - return jobHandle; - } - else - { - var query = GetEntityQuery(new EntityQueryDesc - { - All = new ComponentType[] { typeof(EcsTestData), typeof(EcsTestSharedComp) } - }); - query.SetSharedComponentFilterManaged(new EcsTestSharedComp {value = 10}); - var job = new EcsTestSetComponentValueTo10(); - var jobHandle = job.ScheduleParallel(query, dependsOn); - return jobHandle; - } - } } protected JobEntityTestComponentSystem JobEntityTestSystem => World.GetOrCreateSystemManaged(); @@ -467,11 +424,16 @@ public void SingleArchetype_SingleChunk_Unfiltered() var dependsOn = new JobHandle(); + var query = JobEntityTestSystem.GetEntityQuery(new EntityQueryDesc + { + All = new ComponentType[] { typeof(EcsTestData), typeof(EcsTestData2) } + }); + Measure.Method( () => { for (int i = 0; i < 10000; i++) - dependsOn = JobEntityTestSystem.UpdateComponentValueTo10Job(dependsOn); + dependsOn = JobEntityTestSystem.UpdateComponentValueTo10Job(query, dependsOn); }) .SampleGroup("Scheduling") .Run(); @@ -483,7 +445,7 @@ public void SingleArchetype_SingleChunk_Unfiltered() { for (int i = 0; i < 10000; i++) { - var job = JobEntityTestSystem.UpdateComponentValueTo10Job(dependsOn); + var job = JobEntityTestSystem.UpdateComponentValueTo10Job(query, dependsOn); job.Complete(); } }) @@ -513,11 +475,17 @@ public void SingleArchetype_TwoChunks_Filtered() var dependsOn = new JobHandle(); + var query = JobEntityTestSystem.GetEntityQuery(new EntityQueryDesc + { + All = new ComponentType[] { typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestSharedComp) } + }); + query.SetSharedComponentFilterManaged(new EcsTestSharedComp {value = 10}); + Measure.Method( () => { for (int i = 0; i < 10000; i++) - dependsOn = JobEntityTestSystem.UpdateOnlyComponentValueTo10Job_WithSharedComponentFilter_10(dependsOn); + dependsOn = JobEntityTestSystem.UpdateComponentValueTo10Job(query, dependsOn); }) .SampleGroup("Scheduling") .Run(); @@ -529,10 +497,9 @@ public void SingleArchetype_TwoChunks_Filtered() { for (int i = 0; i < 10000; i++) { - var job = JobEntityTestSystem.UpdateOnlyComponentValueTo10Job_WithSharedComponentFilter_10(dependsOn); + var job = JobEntityTestSystem.UpdateComponentValueTo10Job(query, dependsOn); job.Complete(); } - }) .SampleGroup("ScheduleAndRun") .Run(); @@ -559,11 +526,18 @@ public void SingleArchetype_MultipleChunks_Filtered() } var dependsOn = new JobHandle(); + + var query = JobEntityTestSystem.GetEntityQuery(new EntityQueryDesc + { + All = new ComponentType[] { typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestSharedComp) } + }); + query.SetSharedComponentFilterManaged(new EcsTestSharedComp {value = 10}); + Measure.Method( () => { for (int i = 0; i < 10000; i++) - dependsOn = JobEntityTestSystem.UpdateFirstComponentValueTo10Job_WithSharedComponentFilter_0(withEcsTestData2: true, dependsOn); + dependsOn = JobEntityTestSystem.UpdateComponentValueTo10Job(query, dependsOn); }) .SampleGroup("Scheduling") @@ -576,7 +550,7 @@ public void SingleArchetype_MultipleChunks_Filtered() { for (int i = 0; i < 10000; i++) { - var job = JobEntityTestSystem.UpdateFirstComponentValueTo10Job_WithSharedComponentFilter_0(withEcsTestData2: true); + var job = JobEntityTestSystem.UpdateComponentValueTo10Job(query, default); job.Complete(); } }) @@ -622,12 +596,18 @@ public void MultipleArchetype_MultipleChunks_Filtered() var dependsOn = new JobHandle(); + var query = JobEntityTestSystem.GetEntityQuery(new EntityQueryDesc + { + All = new ComponentType[] { typeof(EcsTestData), typeof(EcsTestSharedComp) } + }); + query.SetSharedComponentFilterManaged(new EcsTestSharedComp {value = 10}); + Measure.Method( () => { for (int i = 0; i < 10000; i++) { - dependsOn = JobEntityTestSystem.UpdateFirstComponentValueTo10Job_WithSharedComponentFilter_0(withEcsTestData2: false, dependsOn); + dependsOn = JobEntityTestSystem.UpdateComponentValueTo10Job(query, dependsOn); } }) .SampleGroup("Scheduling") @@ -640,7 +620,7 @@ public void MultipleArchetype_MultipleChunks_Filtered() { for (int i = 0; i < 10000; i++) { - var job = JobEntityTestSystem.UpdateFirstComponentValueTo10Job_WithSharedComponentFilter_0(withEcsTestData2: false); + var job = JobEntityTestSystem.UpdateComponentValueTo10Job(query, default); job.Complete(); } }) diff --git a/Unity.Entities.PerformanceTests/ManageObjectUtilityPerformanceTests.cs b/Unity.Entities.PerformanceTests/ManageObjectUtilityPerformanceTests.cs index a219006..98bf373 100644 --- a/Unity.Entities.PerformanceTests/ManageObjectUtilityPerformanceTests.cs +++ b/Unity.Entities.PerformanceTests/ManageObjectUtilityPerformanceTests.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using NUnit.Framework; using Unity.Collections; using Unity.Mathematics; @@ -6,7 +6,7 @@ namespace Unity.Entities.PerformanceTests { -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS [TestFixture] [Category("Performance")] public class ManageObjectUtilityPerformanceTests @@ -82,4 +82,4 @@ public void GatherBlobAssetReferencesPerformanceTest() } } #endif -} \ No newline at end of file +} diff --git a/Unity.Entities.TestComponents/MockTransformBaker.cs b/Unity.Entities.TestComponents/MockTransformBaker.cs new file mode 100644 index 0000000..304bbb5 --- /dev/null +++ b/Unity.Entities.TestComponents/MockTransformBaker.cs @@ -0,0 +1,14 @@ +using System; +using UnityEngine; + +namespace Unity.Entities.Tests +{ + [DisableAutoCreation] + public class MockDataAuthoringBaker_TransformBaker : Baker + { + public override void Bake(Transform authoring) + { + GetEntity(TransformUsageFlags.Dynamic); + } + } +} diff --git a/Unity.Entities.Editor/EntityNameBuildPipelineCustomizer.cs.meta b/Unity.Entities.TestComponents/MockTransformBaker.cs.meta similarity index 83% rename from Unity.Entities.Editor/EntityNameBuildPipelineCustomizer.cs.meta rename to Unity.Entities.TestComponents/MockTransformBaker.cs.meta index 0d143f5..b43dec7 100644 --- a/Unity.Entities.Editor/EntityNameBuildPipelineCustomizer.cs.meta +++ b/Unity.Entities.TestComponents/MockTransformBaker.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: 8345cef401937e04abc2576c919219b1 +guid: de9511373b34e2640b06a636d8ee479b MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Unity.Entities.TestComponents/MockTransformDependency.cs b/Unity.Entities.TestComponents/MockTransformDependency.cs new file mode 100644 index 0000000..c035af3 --- /dev/null +++ b/Unity.Entities.TestComponents/MockTransformDependency.cs @@ -0,0 +1,15 @@ +using System; +using UnityEngine; + +namespace Unity.Entities.Tests +{ + [DisableAutoCreation] + public class MockDataAuthoringBaker_TransformDependencyBaker : Baker + { + public override void Bake(MockDataAuthoring authoring) + { + GetEntity(TransformUsageFlags.Dynamic); + GetComponent(); + } + } +} diff --git a/Unity.Entities.Hybrid/GameObjectConversion/ConversionSystemFilterSettings.cs.meta b/Unity.Entities.TestComponents/MockTransformDependency.cs.meta similarity index 83% rename from Unity.Entities.Hybrid/GameObjectConversion/ConversionSystemFilterSettings.cs.meta rename to Unity.Entities.TestComponents/MockTransformDependency.cs.meta index 400b9bf..038bf88 100644 --- a/Unity.Entities.Hybrid/GameObjectConversion/ConversionSystemFilterSettings.cs.meta +++ b/Unity.Entities.TestComponents/MockTransformDependency.cs.meta @@ -1,5 +1,5 @@ fileFormatVersion: 2 -guid: e1fce71dc3b14c542a493c53778454bb +guid: eb01284e3db6fa746901e9e7de3f4938 MonoImporter: externalObjects: {} serializedVersion: 2 diff --git a/Unity.Entities.TestComponents/ReorderComponentAuthoring.cs b/Unity.Entities.TestComponents/ReorderComponentAuthoring.cs new file mode 100644 index 0000000..8151f00 --- /dev/null +++ b/Unity.Entities.TestComponents/ReorderComponentAuthoring.cs @@ -0,0 +1,45 @@ +using UnityEngine; + +namespace Unity.Entities.Tests +{ + public struct ReorderTestComponent : IComponentData + { + public int Value; + } + + [AddComponentMenu("")] + public class ReorderComponentAuthoring : MonoBehaviour + { + class Baker : Baker + { + public override void Bake(ReorderComponentAuthoring authoring) + { + var value = GetComponent().Value; + var entity = GetEntity(TransformUsageFlags.None); + AddComponent(entity, new ReorderTestComponent{Value = value}); + } + } + } + + + public class ParentTestClass : MonoBehaviour + { + public int Value = 0; + } + + public class ChildATestClass : ParentTestClass + { + public ChildATestClass() + { + Value = 1; + } + } + + public class ChildBTestClass : ParentTestClass + { + public ChildBTestClass() + { + Value = 2; + } + } +} diff --git a/Unity.Entities.TestComponents/ReorderComponentAuthoring.cs.meta b/Unity.Entities.TestComponents/ReorderComponentAuthoring.cs.meta new file mode 100644 index 0000000..74d84ce --- /dev/null +++ b/Unity.Entities.TestComponents/ReorderComponentAuthoring.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 6899d575fca54c2e8b375ac30c0cc6c8 +timeCreated: 1681386242 \ No newline at end of file diff --git a/Unity.Entities.TestComponents/SubSceneLoadTestAssetAuthoring.cs b/Unity.Entities.TestComponents/SubSceneLoadTestAssetAuthoring.cs index db7f3a3..61825ca 100644 --- a/Unity.Entities.TestComponents/SubSceneLoadTestAssetAuthoring.cs +++ b/Unity.Entities.TestComponents/SubSceneLoadTestAssetAuthoring.cs @@ -11,7 +11,7 @@ public class SubSceneLoadTestAssetAuthoring : MonoBehaviour public Object Asset; } -#if !NET_DOTS && !UNITY_DISABLE_MANAGED_COMPONENTS +#if !UNITY_DISABLE_MANAGED_COMPONENTS public class SubSceneLoadTestAssetComponent : IComponentData { public Object Asset; @@ -22,7 +22,7 @@ public class SubSceneLoadTestAssetBaker : Baker { public override void Bake(SubSceneLoadTestAssetAuthoring authoring) { -#if !NET_DOTS && !UNITY_DISABLE_MANAGED_COMPONENTS +#if !UNITY_DISABLE_MANAGED_COMPONENTS // This test shouldn't require transform components var entity = GetEntity(TransformUsageFlags.None); AddComponentObject(entity, new SubSceneLoadTestAssetComponent diff --git a/Unity.Entities.TestComponents/SubSceneLoadTestManagedAuthoring.cs b/Unity.Entities.TestComponents/SubSceneLoadTestManagedAuthoring.cs index 540876c..ca5f1c9 100644 --- a/Unity.Entities.TestComponents/SubSceneLoadTestManagedAuthoring.cs +++ b/Unity.Entities.TestComponents/SubSceneLoadTestManagedAuthoring.cs @@ -15,7 +15,7 @@ public class SubSceneLoadTestManagedAuthoring : MonoBehaviour } -#if !NET_DOTS && !UNITY_DISABLE_MANAGED_COMPONENTS +#if !UNITY_DISABLE_MANAGED_COMPONENTS public class SubSceneLoadTestManagedComponent : IComponentData { // Managed components do not support BlobAssetReference typed fields, hence not tested @@ -30,7 +30,7 @@ public class SubSceneLoadTestManagedBaker : Baker + { + public override void Bake(TestComponentForceBakingOnDisabledAuthoring authoring) + { + // This test shouldn't require transform components + var entity = GetEntity(TransformUsageFlags.None); + AddComponent(entity); + } + } + + class NotForcedBaker : Baker + { + public override void Bake(TestComponentForceBakingOnDisabledAuthoring authoring) + { + // This test shouldn't require transform components + var entity = GetEntity(TransformUsageFlags.None); + AddComponent(entity); + } + } + } +} diff --git a/Unity.Entities.TestComponents/TestComponentForceBakingOnDisabledAuthoring.cs.meta b/Unity.Entities.TestComponents/TestComponentForceBakingOnDisabledAuthoring.cs.meta new file mode 100644 index 0000000..fd7352f --- /dev/null +++ b/Unity.Entities.TestComponents/TestComponentForceBakingOnDisabledAuthoring.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 033f0442bc234b8cbc58462eb1361365 +timeCreated: 1682674292 \ No newline at end of file diff --git a/Unity.Entities.Tests/ArchetypeChunkArrayTests.cs b/Unity.Entities.Tests/ArchetypeChunkArrayTests.cs index 99e4dc1..727e647 100644 --- a/Unity.Entities.Tests/ArchetypeChunkArrayTests.cs +++ b/Unity.Entities.Tests/ArchetypeChunkArrayTests.cs @@ -792,7 +792,7 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE } } - [Test,DotsRuntimeFixme] + [Test] public void ACS_UntypedBuffersInvalidationWorks() { var entity = m_Manager.CreateEntity(); @@ -1206,11 +1206,6 @@ public void ComponentTypeHandle_ISystem_UpdateWorks() World.Update(); } - // These tests require: - // - JobsDebugger support for static safety IDs (added in 2020.1) - // - Asserting throws - // - JobsDebugger support for testing for nested native containers -#if !UNITY_DOTSRUNTIME struct ComponentTypeHandleContainerJob : IJob { public ComponentTypeHandle handle; @@ -1272,7 +1267,7 @@ public void ComponentTypeHandle_InJob_Works() Assert.DoesNotThrow(() => job.Schedule().Complete()); } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void ComponentTypeHandle_UseAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1289,7 +1284,7 @@ public void ComponentTypeHandle_UseAfterStructuralChange_ThrowsCustomErrorMessag "ComponentTypeHandle which has been invalidated by a structural change.")); } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void ComponentTypeHandle_UseFromJobAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1363,7 +1358,7 @@ public void DynamicComponentTypeHandle_InJob_Works() Assert.DoesNotThrow(() => job.Schedule().Complete()); } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void DynamicComponentTypeHandle_UseAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1379,7 +1374,7 @@ public void DynamicComponentTypeHandle_UseAfterStructuralChange_ThrowsCustomErro .With.Message.Contains("Unity.Entities.DynamicComponentTypeHandle which has been invalidated by a structural change")); } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void DynamicComponentTypeHandle_UseFromJobAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1451,7 +1446,7 @@ public void DynamicSharedComponentTypeHandle_InJob_Works() Assert.DoesNotThrow(() => job.Schedule().Complete()); } - [Test, DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void DynamicSharedComponentTypeHandle_UseAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1471,7 +1466,7 @@ public void DynamicSharedComponentTypeHandle_UseAfterStructuralChange_ThrowsCust #endif } - [Test, DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void DynamicSharedComponentTypeHandle_UseFromJobAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1555,7 +1550,7 @@ public void BufferTypeHandle_InJob_Works() Assert.DoesNotThrow(() => job.Schedule().Complete()); } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void BufferTypeHandle_UseAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1571,7 +1566,7 @@ public void BufferTypeHandle_UseAfterStructuralChange_ThrowsCustomErrorMessage() "BufferTypeHandle which has been invalidated by a structural change.")); } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void BufferTypeHandle_UseFromJobAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1651,7 +1646,7 @@ public void SharedComponentTypeHandle_InJob_Works() Assert.DoesNotThrow(() => job.Schedule().Complete()); } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void SharedComponentTypeHandle_UseAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1671,7 +1666,7 @@ public void SharedComponentTypeHandle_UseAfterStructuralChange_ThrowsCustomError #endif } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void SharedComponentTypeHandle_UseFromJobAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1751,7 +1746,7 @@ public void Execute() } } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void EntityTypeHandle_UseAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1768,7 +1763,7 @@ public void EntityTypeHandle_UseAfterStructuralChange_ThrowsCustomErrorMessage() "Unity.Entities.EntityTypeHandle which has been invalidated by a structural change.")); } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Relies on static safety id system")] public void EntityTypeHandle_UseFromJobAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -1788,6 +1783,5 @@ public void EntityTypeHandle_UseFromJobAfterStructuralChange_ThrowsCustomErrorMe .With.Message.Contains( "Unity.Entities.EntityTypeHandle UseEntityTypeHandle.ecsTestData which has been invalidated by a structural change.")); } -#endif } } diff --git a/Unity.Entities.Tests/Aspects/AspectTests.cs b/Unity.Entities.Tests/Aspects/AspectTests.cs index 06ea860..a135a76 100644 --- a/Unity.Entities.Tests/Aspects/AspectTests.cs +++ b/Unity.Entities.Tests/Aspects/AspectTests.cs @@ -169,6 +169,14 @@ public void AddComponentRequirementsTo(ref UnsafeList all) all.Add(ComponentType.ReadWrite()); } + public void CompleteDependencyBeforeRO(ref SystemState state) + { + } + + public void CompleteDependencyBeforeRW(ref SystemState state) + { + } + public readonly RefRW Data; } diff --git a/Unity.Entities.Tests/BlobDebugTests.cs b/Unity.Entities.Tests/BlobDebugTests.cs index 7a750c1..cba162f 100644 --- a/Unity.Entities.Tests/BlobDebugTests.cs +++ b/Unity.Entities.Tests/BlobDebugTests.cs @@ -1,4 +1,3 @@ -#if !NET_DOTS using UnityEngine; using NUnit.Framework; using System; @@ -194,4 +193,3 @@ public void BlobAssetReferenceDebugProxy_WorksWithStringNestedInArray() CollectionAssert.AreEqual(new []{"One", "Two", "Three"}, arr.Entries); } } -#endif diff --git a/Unity.Entities.Tests/BlobificationTests.cs b/Unity.Entities.Tests/BlobificationTests.cs index 4dc99ef..51d8286 100644 --- a/Unity.Entities.Tests/BlobificationTests.cs +++ b/Unity.Entities.Tests/BlobificationTests.cs @@ -16,13 +16,8 @@ using Unity.Entities.LowLevel.Unsafe; using Unity.IO.LowLevel.Unsafe; -#if UNITY_DOTSRUNTIME -using Unity.Runtime.IO; -#endif - public partial class BlobTests : ECSTestsFixture { -#if !UNITY_DOTSRUNTIME BlobTestSystemEFE _blobTestSystemEFE => World.CreateSystemManaged(); partial class BlobTestSystemEFE : SystemBase { @@ -69,12 +64,11 @@ public unsafe void Execute(ref ComponentWithBlobData componentWithBlobData) } protected override void OnUpdate() {} } -#endif - //@TODO: Test Prevent NativeArray and other containers inside of Blob data - //@TODO: Test Prevent BlobPtr, BlobArray onto job struct - //@TODO: Various tests trying to break the Allocator. eg. mix multiple BlobAllocator in the same BlobRoot... + //@TODO: Test Prevent NativeArray and other containers inside of Blob data + //@TODO: Test Prevent BlobPtr, BlobArray onto job struct + //@TODO: Various tests trying to break the Allocator. eg. mix multiple BlobAllocator in the same BlobRoot... - struct MyData + struct MyData { public BlobArray floatArray; public BlobPtr nullPtr; @@ -216,7 +210,7 @@ public unsafe void BlobAccessAfterReleaseThrows() Assert.Throws(() => { var p = blobCopy.Value.embeddedFloat; }); Assert.Throws(() => { blobCopy.Dispose(); }); - Assert.Throws(() => { blob.Dispose(); }); + Assert.Throws(() => { blob.Dispose(); }); } struct ComponentWithBlobData : IComponentData @@ -243,7 +237,6 @@ public void BurstedConstructionAndAccess() new ConstructAccessAndDisposeBlobData().Schedule().Complete(); } -#if !UNITY_DOTSRUNTIME [Test] public void ReadAndDestroyBlobDataFromBurstJob() { @@ -279,7 +272,6 @@ public void DestroyedBlobAccessFromEntityJobThrows() var handle = _blobTestSystemIJobEntity.ValidateBlobInComponent(expectException: true); handle.Complete(); } -#endif [Test] public void BlobAssetReferenceIsComparable() @@ -607,7 +599,7 @@ NativeArray CreateUniqueBlob() const int kVersion = 51; const int kIncorrectVersion = 13; -#if !UNITY_DOTSRUNTIME && !UNITY_GAMECORE // (disabled as gamecore has permission issues DOTS-7038) +#if !UNITY_GAMECORE // (disabled as gamecore has permission issues DOTS-7038) [Test] // Unstable on PS4: https://jira.unity3d.com/browse/DOTS-7693 [UnityEngine.TestTools.UnityPlatform(exclude = new [] { RuntimePlatform.PS4 })] @@ -781,7 +773,6 @@ public void BlobArray_WithNestedBlobPointers_ToArray_ThrowsInvalidOperationExcep Assert.Throws(() => blobRef.Value.ToArray(), "No exception was thrown when creating array copy from BlobArray>."); } -#if !UNITY_DOTSRUNTIME // Not running on DOTS runtime because the temp allocator isn't predictably generating contiguous allocations // and only contiguous allocations can trigger the issue that this regression test is guarding against [Test] @@ -840,5 +831,4 @@ public void BlobBuilder_RegressionTestDOTS7887() // addresses, they usually end up after each other in the finalized blob, causing the off by one error // to be completely harmless. } -#endif } diff --git a/Unity.Entities.Tests/BufferElementDataInstantiateTests.cs b/Unity.Entities.Tests/BufferElementDataInstantiateTests.cs index c2445f2..ad200c2 100644 --- a/Unity.Entities.Tests/BufferElementDataInstantiateTests.cs +++ b/Unity.Entities.Tests/BufferElementDataInstantiateTests.cs @@ -2,10 +2,7 @@ using System.Collections.Generic; using NUnit.Framework; using Unity.Collections; - -#if !UNITY_PORTABLE_TEST_RUNNER using System.Linq; -#endif #pragma warning disable 0649 #pragma warning disable 0219 // assigned but its value is never used @@ -64,10 +61,6 @@ struct MockElement : IBufferElementData public byte Value; } -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1432 - // TODO: IL2CPP_TEST_RUNNER doesn't support the Assert.That / Has behavior - // LINQ code is unsupported as well. [Test] public void DuplicatingEntity_WhenPrototypeHasDynamicBuffer_DoesNotWriteOutOfBounds() { @@ -150,8 +143,6 @@ public void DuplicatingEntity_WhenPrototypeHasDynamicBuffer_DoesNotWriteOutOfBou duplicate.Dispose(); } } - -#endif } } diff --git a/Unity.Entities.Tests/BufferElementDataTests.cs b/Unity.Entities.Tests/BufferElementDataTests.cs index 693e279..9178184 100644 --- a/Unity.Entities.Tests/BufferElementDataTests.cs +++ b/Unity.Entities.Tests/BufferElementDataTests.cs @@ -5,10 +5,7 @@ using System.Collections.Generic; using Unity.Burst.Intrinsics; using Unity.Jobs; - -#if !UNITY_PORTABLE_TEST_RUNNER using System.Linq; -#endif // ******* COPY AND PASTE WARNING ************* diff --git a/Unity.Entities.Tests/ChangeVersionTests.cs b/Unity.Entities.Tests/ChangeVersionTests.cs index b9b5b4b..6c37cd3 100644 --- a/Unity.Entities.Tests/ChangeVersionTests.cs +++ b/Unity.Entities.Tests/ChangeVersionTests.cs @@ -62,7 +62,6 @@ public bool AllEcsTestDataChunksChanged() partial class ChangeVersionTests : ECSTestsFixture { -#if !UNITY_DOTSRUNTIME partial class BumpVersionSystemInJob : SystemBase { JobHandle UpdateEcsTestData2() @@ -85,7 +84,6 @@ protected override void OnCreate() { } } -#endif partial class BumpVersionSystem : SystemBase { @@ -131,7 +129,6 @@ public void CHG_BumpValueChangesChunkTypeVersion() } [Test] - [DotsRuntimeFixme("Name of ECSTestData not supported")] public void GetLastWriterSystemName() { var entity = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsTestData2)); @@ -369,7 +366,6 @@ public void BufferLookup_DidChange_DetectsChanges() entities.Dispose(); } -#if !UNITY_DOTSRUNTIME [Test] public void CHG_SystemVersionJob() { @@ -379,7 +375,5 @@ public void CHG_SystemVersionJob() system.Update(); Assert.AreNotEqual(0, system.LastSystemVersion); } - -#endif } } diff --git a/Unity.Entities.Tests/ChunkChangeVersionTests.cs b/Unity.Entities.Tests/ChunkChangeVersionTests.cs index 4813248..e8bf7df 100644 --- a/Unity.Entities.Tests/ChunkChangeVersionTests.cs +++ b/Unity.Entities.Tests/ChunkChangeVersionTests.cs @@ -214,6 +214,28 @@ public void GetComponentRW_Via_GetBuffer() AssetHasChunkOrderVersion(e1, OldVersion); } + [Test] + public void GetBufferRO_Via_GetBuffer() + { + var e1 = m_Manager.CreateEntity(typeof(EcsIntElement)); + + BumpGlobalSystemVersion(); + m_Manager.GetBuffer(e1, true); + + AssetHasBufferChangeVersion(e1, OldVersion); + } + + [Test] + public void GetBufferRW_Via_GetBuffer() + { + var e1 = m_Manager.CreateEntity(typeof(EcsIntElement)); + + BumpGlobalSystemVersion(); + m_Manager.GetBuffer(e1, false); + + AssetHasBufferChangeVersion(e1, NewVersion); + } + [Test] public void SetSharedComponentDataIndex_Via_Entity() { @@ -304,7 +326,7 @@ public unsafe void ClearMissingReferences() BumpGlobalSystemVersion(); - ChunkDataUtility.ClearMissingReferences(chunk0.m_Chunk); + ChunkDataUtility.ClearMissingReferences(m_Manager.GetCheckedEntityDataAccess()->EntityComponentStore->GetArchetype(chunk0.m_Chunk), chunk0.m_Chunk); AssetHasChangeVersion(e1, OldVersion); AssetHasChangeVersion(e1, NewVersion); @@ -548,7 +570,7 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE } [Test] - public unsafe void GetComponentRO_Via_GetBuffer() + public unsafe void GetComponentRO_Via_GetBufferTypeHandle() { var e0 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsIntElement)); var e1 = m_Manager.CreateEntity(typeof(EcsTestData), typeof(EcsIntElement)); diff --git a/Unity.Entities.Tests/ChunkComponentTests.cs b/Unity.Entities.Tests/ChunkComponentTests.cs index 0517409..ce2124f 100644 --- a/Unity.Entities.Tests/ChunkComponentTests.cs +++ b/Unity.Entities.Tests/ChunkComponentTests.cs @@ -40,7 +40,7 @@ public unsafe void ArchetypeChunk_GetAndSetChunkComponent_ThrowWhenMetaChunkEnti var chunkComponentType = m_Manager.GetComponentTypeHandle(false); var chunk = m_Manager.GetChunk(entity); - Assert.AreEqual(chunk.m_Chunk->metaChunkEntity, Entity.Null); + Assert.AreEqual(chunk.m_Chunk.MetaChunkEntity, Entity.Null); Assert.Throws(() => chunk.SetChunkComponentData(ref chunkComponentType, new EcsTestData2(12))); Assert.Throws(() => chunk.GetChunkComponentData(ref chunkComponentType)); } @@ -53,7 +53,7 @@ public unsafe void ArchetypeChunk_GetAndSetChunkComponent_ThrowWhenComponentMiss var chunkComponentType = m_Manager.GetComponentTypeHandle(false); var chunk = m_Manager.GetChunk(entity); - Assert.AreNotEqual(chunk.m_Chunk->metaChunkEntity, Entity.Null); + Assert.AreNotEqual(chunk.m_Chunk.MetaChunkEntity, Entity.Null); Assert.Throws(() => chunk.GetChunkComponentData(ref chunkComponentType)); Assert.Throws(() => chunk.SetChunkComponentData(ref chunkComponentType, new EcsTestData2(12))); } @@ -189,7 +189,6 @@ public void ProcessMetaChunkComponent() Assert.AreEqual(new float3(10, 10, 10), val.boundsMax); } -#if !UNITY_DOTSRUNTIME partial struct UpdateChunkBoundsJob : IJobEntity { [ReadOnly] public ComponentTypeHandle ChunkComponentTypeHandle; @@ -235,7 +234,6 @@ public void SystemProcessMetaChunkComponent() Assert.AreEqual(new float3(-10, -10, -10), val.boundsMin); Assert.AreEqual(new float3(10, 10, 10), val.boundsMax); } -#endif [Test] public void ChunkHeaderMustBeQueriedExplicitly() diff --git a/Unity.Entities.Tests/CleanupComponentTests.cs b/Unity.Entities.Tests/CleanupComponentTests.cs index 9118549..5a3a712 100644 --- a/Unity.Entities.Tests/CleanupComponentTests.cs +++ b/Unity.Entities.Tests/CleanupComponentTests.cs @@ -363,8 +363,6 @@ struct CleanupShared : ICleanupSharedComponentData public int Value; } -#if !NET_DOTS -// https://unity3d.atlassian.net/browse/DOTSR-1432 [Test] public void CleanupSharedKeepsValueAfterDestroy() { @@ -373,7 +371,5 @@ public void CleanupSharedKeepsValueAfterDestroy() m_Manager.DestroyEntity(entity); EntitiesAssert.ContainsOnly(m_Manager, EntityMatch.Exact(new CleanupShared { Value = 123 })); } - -#endif } } diff --git a/Unity.Entities.Tests/ComponentEnabledBitFeatureTests.cs b/Unity.Entities.Tests/ComponentEnabledBitFeatureTests.cs index 4cf43c7..4537316 100644 --- a/Unity.Entities.Tests/ComponentEnabledBitFeatureTests.cs +++ b/Unity.Entities.Tests/ComponentEnabledBitFeatureTests.cs @@ -374,7 +374,7 @@ public unsafe void GetEnabledMask_AllHaveRequiredComponentDisabled_SkipChunk() var queryChunkCache = queryImpl->GetMatchingChunkCache(); var matchIndex = queryChunkCache.PerChunkMatchingArchetypeIndex->Ptr[0]; var matchingArchetype = queryData->MatchingArchetypes.Ptr[matchIndex]; - var chunk = queryChunkCache.Ptr[0]; + var chunk = queryChunkCache.ChunkIndices[0]; ChunkIterationUtility.GetEnabledMask(chunk, matchingArchetype, out var chunkEnabledMask); Assert.AreEqual(0, chunkEnabledMask.ULong0); Assert.AreEqual(0, chunkEnabledMask.ULong1); @@ -402,7 +402,7 @@ public unsafe void GetEnabledMask_NoneHaveExcludedComponentDisabled_SkipChunk() var queryChunkCache = queryImpl->GetMatchingChunkCache(); var matchIndex = queryChunkCache.PerChunkMatchingArchetypeIndex->Ptr[0]; var matchingArchetype = queryData->MatchingArchetypes.Ptr[matchIndex]; - var chunk = queryChunkCache.Ptr[0]; + var chunk = queryChunkCache.ChunkIndices[0]; ChunkIterationUtility.GetEnabledMask(chunk, matchingArchetype, out var chunkEnabledMask); Assert.AreEqual(0, chunkEnabledMask.ULong0); Assert.AreEqual(0, chunkEnabledMask.ULong1); @@ -431,7 +431,7 @@ public unsafe void GetEnabledMask_SomeHaveRequiredComponentDisabled_HasExpectedM var queryChunkCache = queryImpl->GetMatchingChunkCache(); var matchIndex = queryChunkCache.PerChunkMatchingArchetypeIndex->Ptr[0]; var matchingArchetype = queryData->MatchingArchetypes.Ptr[matchIndex]; - var chunk = queryChunkCache.Ptr[0]; + var chunk = queryChunkCache.ChunkIndices[0]; // The chunk should be half-full, with the first entity not enabled (it has the required component disabled) ChunkIterationUtility.GetEnabledMask(chunk, matchingArchetype, out var chunkEnabledMask); Assert.AreEqual(0xFFFFFFFFFFFFFFFE, chunkEnabledMask.ULong0); @@ -457,7 +457,7 @@ public unsafe void GetEnabledMask_SomeHaveExcludedComponentDisabled_HasExpectedM var queryChunkCache = queryImpl->GetMatchingChunkCache(); var matchIndex = queryChunkCache.PerChunkMatchingArchetypeIndex->Ptr[0]; var matchingArchetype = queryData->MatchingArchetypes.Ptr[matchIndex]; - var chunk = queryChunkCache.Ptr[0]; + var chunk = queryChunkCache.ChunkIndices[0]; // All entities have the required component enabled, but only the first has the excluded component disabled ChunkIterationUtility.GetEnabledMask(chunk, matchingArchetype, out var chunkEnabledMask); Assert.AreEqual(0x1, chunkEnabledMask.ULong0); @@ -483,7 +483,7 @@ public unsafe void GetEnabledMask_SomeHaveDisabledComponentEnabled_HasExpectedMa var queryChunkCache = queryImpl->GetMatchingChunkCache(); var matchIndex = queryChunkCache.PerChunkMatchingArchetypeIndex->Ptr[0]; var matchingArchetype = queryData->MatchingArchetypes.Ptr[matchIndex]; - var chunk = queryChunkCache.Ptr[0]; + var chunk = queryChunkCache.ChunkIndices[0]; // All entities have the required component enabled, but only the first has the excluded component disabled ChunkIterationUtility.GetEnabledMask(chunk, matchingArchetype, out var chunkEnabledMask); Assert.AreEqual(0x1, chunkEnabledMask.ULong0); @@ -512,7 +512,7 @@ public unsafe void GetEnabledMask_AllHaveRequiredComponentEnabledAndExcludedComp var queryChunkCache = queryImpl->GetMatchingChunkCache(); var matchIndex = queryChunkCache.PerChunkMatchingArchetypeIndex->Ptr[0]; var matchingArchetype = queryData->MatchingArchetypes.Ptr[matchIndex]; - var chunk = queryChunkCache.Ptr[0]; + var chunk = queryChunkCache.ChunkIndices[0]; // All entities have the required component enabled, but only the first has the excluded component disabled ChunkIterationUtility.GetEnabledMask(chunk, matchingArchetype, out var chunkEnabledMask); Assert.AreEqual(ulong.MaxValue, chunkEnabledMask.ULong0); diff --git a/Unity.Entities.Tests/ComponentEnabledBitTests.cs b/Unity.Entities.Tests/ComponentEnabledBitTests.cs index ffe0535..e50a9a4 100644 --- a/Unity.Entities.Tests/ComponentEnabledBitTests.cs +++ b/Unity.Entities.Tests/ComponentEnabledBitTests.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#if !NET_DOTS && !UNITY_DOTSRUNTIME // DOTS Runtimes does not support regex using System.Text.RegularExpressions; -#endif using NUnit.Framework; using Unity.Burst; using Unity.Burst.Intrinsics; @@ -231,19 +229,27 @@ protected override void OnUpdate() public unsafe void SetComponentEnabled_ChunkChangeVersion_IsChanged() { var ecs = m_Manager.GetCheckedEntityDataAccess()->EntityComponentStore; + + uint ChangeVersion(Entity entity, int typeIndexInArchetype) + { + var archetype = ecs->GetArchetype(entity); + var chunk = ecs->GetChunk(entity); + return archetype->Chunks.GetChangeVersion(typeIndexInArchetype, chunk.ListIndex); + } + var archetype = m_Manager.CreateArchetype(typeof(EcsTestDataEnableable)); var ent = m_Manager.CreateEntity(archetype); var typeIndex = TypeManager.GetTypeIndex(typeof(EcsTestDataEnableable)); var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(archetype.Archetype, typeIndex); - var versionBefore = ecs->GetChunk(ent)->GetChangeVersion(typeIndexInArchetype); + var versionBefore = ChangeVersion(ent, typeIndexInArchetype); // Force a system update on unrelated entities to bump the global system version var ent2 = m_Manager.CreateEntity(typeof(EcsTestDataEnableable2)); var sys = World.CreateSystemManaged(); sys.Update(); - var versionAfterUpdate = ecs->GetChunk(ent)->GetChangeVersion(typeIndexInArchetype); + var versionAfterUpdate = ChangeVersion(ent, typeIndexInArchetype); Assert.AreEqual(versionBefore, versionAfterUpdate, "Chunk's change version should be the same after unrelated system update"); m_Manager.SetComponentEnabled(ent, false); - var versionAfterSet = ecs->GetChunk(ent)->GetChangeVersion(typeIndexInArchetype); + var versionAfterSet = ChangeVersion(ent, typeIndexInArchetype); Assert.AreNotEqual(versionBefore, versionAfterSet, "Chunk's change version should be different after SetComponentEnabled()"); } @@ -742,12 +748,13 @@ public unsafe void Serialization_PreservesBitValues([Values(1, 4)] int chunkCoun for (int chunkIndex = 0; chunkIndex < chunks.Count; ++chunkIndex) { var chunk = chunks[chunkIndex]; - for (int i = 0; i < chunk->Count; ++i) + var entityArray = (Entity*)chunk.Buffer; + + for (int i = 0, count = chunk.Count; i < count; ++i) { var entityIndexInQuery = chunkIndex * archetype.ChunkCapacity + i; var value = GetTestEntityShouldBeEnabled(entityIndexInQuery, chunkIndex); - var entityArray = (Entity*) chunk->Buffer; Assert.AreEqual(value, deserializeManager.IsComponentEnabled(entityArray[i])); } @@ -801,12 +808,13 @@ public unsafe void Serialization_LargeEntity_PreservesBitValues([Values(4)] int for (int chunkIndex = 0; chunkIndex < chunks.Count; ++chunkIndex) { var chunk = chunks[chunkIndex]; - for (int i = 0; i < chunk->Count; ++i) + var entityArray = (Entity*) chunk.Buffer; + + for (int i = 0, count = chunk.Count; i < count; ++i) { var entityIndexInQuery = chunkIndex * archetype.ChunkCapacity + i; var value = GetTestEntityShouldBeEnabled(entityIndexInQuery, chunkIndex); - var entityArray = (Entity*) chunk->Buffer; Assert.AreEqual(value, deserializeManager.IsComponentEnabled(entityArray[i])); } @@ -1024,6 +1032,26 @@ public unsafe void EntityQuery_WithEnableableComponents_CreatesCorrectMatchingAr Assert.AreEqual(0, b->EnableableComponentsCount_Any); Assert.AreEqual(1, b->EnableableComponentsCount_Disabled); } + + // Enableable components in the WithPresent set should match the expected archetypes, but don't affect the + // enableable component counts. + using (var query = new EntityQueryBuilder(Allocator.Temp).WithAll().WithPresent().Build(m_Manager)) + { + var queryData = query._GetImpl()->_QueryData; + var matchingArchetypes = queryData->MatchingArchetypes; + Assert.AreEqual(2, matchingArchetypes.Length); + + var a = matchingArchetypes.Ptr[0]; + Assert.AreEqual(0, a->EnableableComponentsCount_All); + Assert.AreEqual(0, a->EnableableComponentsCount_None); + Assert.AreEqual(0, a->EnableableComponentsCount_Any); + Assert.AreEqual(0, a->EnableableComponentsCount_Disabled); + var b = matchingArchetypes.Ptr[1]; + Assert.AreEqual(0, b->EnableableComponentsCount_All); + Assert.AreEqual(0, b->EnableableComponentsCount_None); + Assert.AreEqual(0, b->EnableableComponentsCount_Any); + Assert.AreEqual(0, b->EnableableComponentsCount_Disabled); + } } private unsafe NativeArray CreateChunks(ref EntityManager manager, EntityArchetype archetype, int chunkCount, Allocator allocator) @@ -1988,8 +2016,6 @@ public void EntitiesForEach_WithStructuralChanges_WithEnableableComponents_Match sys.ProcessedEntities.ToArray(Allocator.Temp).ToArray()); } - -#if !NET_DOTS && !UNITY_DOTSRUNTIME // DOTS Runtimes does not support regex struct DataJob_WriteBits_ComponentLookup : IJobChunk { [ReadOnly]public ComponentLookup EnableableType; @@ -2123,6 +2149,5 @@ public void WritingBitsToReadOnlyBuffer_TriggersSafetySystem_ArchetypeChunk() LogAssert.Expect(LogType.Exception, new Regex("InvalidOperationException")); new BufferJob_WritesBits_ArchetypeChunk(){ EnableableType = m_Manager.GetBufferTypeHandle(true)}.Run(queryRO); } -#endif } } diff --git a/Unity.Entities.Tests/ComponentOrderVersionTests.cs b/Unity.Entities.Tests/ComponentOrderVersionTests.cs index 46f8fed..a46cb94 100644 --- a/Unity.Entities.Tests/ComponentOrderVersionTests.cs +++ b/Unity.Entities.Tests/ComponentOrderVersionTests.cs @@ -249,7 +249,6 @@ public void DestroySharedComponentEntity() Assert.LessOrEqual(3, m_Manager.GetSharedComponentOrderVersion(sharedData)); } - #if !NET_DOTS [Test] public void GetSharedComponentOrderVersionIncrementingWithManaged([Values(0, 1)]int value) { @@ -286,7 +285,6 @@ public void GetSharedComponentOrderVersionIncrementingWithManaged([Values(0, 1)] Assert.AreEqual(unaffectedVersion, m_Manager.GetSharedComponentOrderVersionManaged(new ManagedSharedData2(unaffectedSharedValue))); } -#endif [Test] public void GetUnmanagedSharedComponentOrderVersionIncrementing([Values(0, 1)]int value) diff --git a/Unity.Entities.Tests/ComponentSystemGroupTests.cs b/Unity.Entities.Tests/ComponentSystemGroupTests.cs index 68eb646..a1dda24 100644 --- a/Unity.Entities.Tests/ComponentSystemGroupTests.cs +++ b/Unity.Entities.Tests/ComponentSystemGroupTests.cs @@ -10,11 +10,8 @@ using Unity.Burst; using Unity.Collections; using Unity.Transforms; - -#if !UNITY_PORTABLE_TEST_RUNNER using System.Text.RegularExpressions; using System.Linq; -#endif namespace Unity.Entities.Tests { @@ -232,7 +229,6 @@ protected override void OnUpdate() } } -#if !UNITY_DOTSRUNTIME // DOTS Runtime does not eat the Exception so this test can not pass (the 3rd assert will always fail) [Test] public void SystemInGroupThrows_LaterSystemsRun() { @@ -250,7 +246,6 @@ public void SystemInGroupThrows_LaterSystemsRun() Assert.AreEqual(0, child2.CompleteUpdateCount); Assert.AreEqual(1, child3.CompleteUpdateCount); } -#endif [Test] public void SystemThrows_SystemNotRemovedFromUpdate() @@ -259,17 +254,9 @@ public void SystemThrows_SystemNotRemovedFromUpdate() var child = World.CreateSystemManaged(); parent.AddSystemToUpdateList(child); LogAssert.Expect(LogType.Exception, new Regex(child.ExceptionMessage)); -#if UNITY_DOTSRUNTIME - Assert.Throws(() => parent.Update()); -#else parent.Update(); -#endif LogAssert.Expect(LogType.Exception, new Regex(child.ExceptionMessage)); -#if UNITY_DOTSRUNTIME - Assert.Throws(() => parent.Update()); -#else parent.Update(); -#endif LogAssert.NoUnexpectedReceived(); Assert.AreEqual(0, child.CompleteUpdateCount); @@ -575,13 +562,9 @@ public void UpdateInGroup_TargetNotASystem_Throws() { World w = new World("Test World"); -#if !UNITY_PORTABLE_TEST_RUNNER // In hybrid, IsSystemAGroup() returns false for non-system inputs Assert.That(() => DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(w, typeof(GroupIsntAComponentSystem)), Throws.InvalidOperationException.With.Message.Contains("must be derived from ComponentSystemGroup")); -#else - Assert.Throws(() => DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(w, typeof(GroupIsntAComponentSystem))); -#endif w.Dispose(); } @@ -595,14 +578,8 @@ public partial class GroupIsntAComponentSystemGroup : EmptySystem public void UpdateInGroup_TargetNotAGroup_Throws() { World w = new World("Test World"); -#if NET_DOTS - Assert.Throws(() => - DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(w, - typeof(GroupIsntAComponentSystemGroup))); -#else Assert.That(() => DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(w, typeof(GroupIsntAComponentSystemGroup)), Throws.InvalidOperationException.With.Message.Contains("must be derived from ComponentSystemGroup")); -#endif w.Dispose(); } @@ -616,13 +593,8 @@ public void UpdateInGroup_OrderFirstAndOrderLast_Throws() { World w = new World("Test World"); var systemTypes = new[] {typeof(FirstAndLast), typeof(TestGroup)}; -#if NET_DOTS - Assert.Throws(() => - DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(w, systemTypes)); -#else Assert.That(() => DefaultWorldInitialization.AddSystemsToRootLevelSystemGroups(w, systemTypes), Throws.InvalidOperationException.With.Message.Contains("can not specify both OrderFirst=true and OrderLast=true")); -#endif w.Dispose(); } @@ -694,8 +666,6 @@ public void ComponentSystemSorter_ValidUpdateConstraints_SortCorrectlyWithNoWarn LogAssert.NoUnexpectedReceived(); } -#if !UNITY_DOTSRUNTIME_IL2CPP - // Invalid constraints [UpdateInGroup(typeof(TestGroup), OrderFirst = true)] public partial class DummyFirstSystem : EmptySystem { } @@ -804,8 +774,6 @@ public void ComponentSystemSorter_OrderLastUpdateBeforeOrderFirst_WarnAndIgnoreC CollectionAssert.AreEqual(systems, parent.ManagedSystems); } -#endif - [UpdateInGroup(typeof(TestGroup), OrderFirst = true)] public partial class OFL_A : EmptySystem { @@ -1253,7 +1221,7 @@ public void SetUp() SystemTypeList.Add(idx); } - SystemOrderInfoMap = new NativeParallelHashMap(SystemTypeList.Length, Allocator.Persistent); + SystemOrderInfoMap = new NativeParallelHashMap(SystemTypeList.Length, Allocator.Persistent); SystemSequenceNo = 0; // Make a custom world and manually add the systems @@ -1268,12 +1236,14 @@ World CreateWorldWithSystems(NativeList systemList) // Normally this happens when fetching all systems based on world filter flags // but since we want to be very specific about which systems are registered with the world while using the machinery to do so // we sort them here like what would happen normally + var copy = new NativeList(systemList.Length, Allocator.Temp); copy.CopyFrom(systemList); TypeManager.SortSystemTypesInCreationOrder(copy); DefaultWorldInitialization.AddSystemToRootLevelSystemGroupsInternal(world, copy); + return world; } @@ -1459,7 +1429,6 @@ public void CreateBefore_CreateAfter_SanityCheck() systemList.CopyFrom(SystemTypeList); TypeManager.SortSystemTypesInCreationOrder(systemList); - // Confirm CreateBefore|After attributes influence create order { var a = (ulong)TestWorld.GetExistingSystemManaged().m_StatePtr; diff --git a/Unity.Entities.Tests/ComponentSystemTests.cs b/Unity.Entities.Tests/ComponentSystemTests.cs index eabc6a0..a855f3d 100644 --- a/Unity.Entities.Tests/ComponentSystemTests.cs +++ b/Unity.Entities.Tests/ComponentSystemTests.cs @@ -86,8 +86,6 @@ public void Create() Assert.IsTrue(system.Created); } -#if !UNITY_PORTABLE_TEST_RUNNER - // TODO: IL2CPP_TEST_RUNNER can't handle Assert.That Throws #if ENABLE_UNITY_COLLECTIONS_CHECKS [Test] public void ComponentSystem_CheckExistsAfterDestroy_CorrectMessage() @@ -108,8 +106,6 @@ public void ComponentSystem_CheckExistsBeforeCreate_CorrectMessage() Assert.That(() => { incompleteSystem.ShouldRunSystem(); }, Throws.InvalidOperationException.With.Message.Contains("initialized")); } - -#endif #endif [Test] @@ -143,13 +139,11 @@ public void InheritedSystem() Assert.IsFalse(system.Created); } -#if !UNITY_DOTSRUNTIME [Test] public void CreateNonSystemThrows() { Assert.Throws(() => { World.CreateSystemManaged(typeof(Entity)); }); } -#endif [Test] public void GetOrCreateNonSystemThrows() @@ -603,7 +597,6 @@ public void EntityTypeHandle_SystemBase_UpdateWorks() World.Update(); } -#if !UNITY_DOTSRUNTIME // DOTSR doesn't support GetCustomAttributes() [DisableAutoCreation] class ParentWithDisableAutoCreation { @@ -923,7 +916,6 @@ public void BufferLookup_TryGetBuffer_FullyUpdatesLookupCache() // Before the fix, this will pass IndexInArchetype=-1 to SetChangeVersion(), which asserts / stomps unrelated memory. Assert.DoesNotThrow(() => { buffer = lookup[entityA]; }); } -#endif #if UNITY_ENTITIES_RUNTIME_TOOLING partial class SystemThatTakesTime : SystemBase @@ -967,8 +959,6 @@ public void RuntimeToolingSystemTiming() } #endif -#if !UNITY_DOTSRUNTIME - public partial class NonPreservedTestSystem : SystemBase { public string m_Test; @@ -989,7 +979,6 @@ public partial class PreservedTestSystem : SystemBase public PreservedTestSystem(string inputParam) { m_Test = inputParam; } protected override void OnUpdate() { } } -#endif partial struct UnmanagedSystemWithSyncPointAfterSchedule : ISystem { @@ -1572,11 +1561,6 @@ public void SystemBase_WorldSystemFiltering_Exists() Assert.AreEqual((WorldSystemFilterFlags)(1 << 20), TypeManager.GetSystemFilterFlags(typeof(WorldSystemFilterSystem))); } -#if !UNITY_DOTSRUNTIME - /* - Fails with Burst compile errors on DOTS RT use of try/catch - Once we have a shared job system between Big Unity and DOTS RT, we should re-evaluate. - */ [BurstCompile] partial struct BurstCompiledUnmanagedSystem : ISystem { @@ -1616,9 +1600,7 @@ public void OnUpdate(ref SystemState state) state.EntityManager.CreateEntity(); } } -#endif -#if !UNITY_DOTSRUNTIME // Reflection required struct UnmanagedSystemHandleData : IComponentData { public SystemHandle other; @@ -1656,7 +1638,7 @@ public void UnmanagedSystemRefsBatchCreateWorks() tmp.Add(TypeManager.GetSystemTypeIndex()); World.Unmanaged.GetOrCreateUnmanagedSystems(tmp); - + var sysA = World.Unmanaged.GetExistingUnmanagedSystem(); var sysB = World.Unmanaged.GetExistingUnmanagedSystem(); @@ -1666,6 +1648,80 @@ public void UnmanagedSystemRefsBatchCreateWorks() Assert.IsTrue(World.EntityManager.GetComponentData(sysA).other == sysB); Assert.IsTrue(World.EntityManager.GetComponentData(sysB).other == sysA); } -#endif + + partial class BufferLookup_CacheRegressionTest_System : SystemBase + { + Entity m_EntityA; + Entity m_EntityB; + BufferLookup m_BufferLookup; + + protected override void OnCreate() + { + m_EntityA = EntityManager.CreateEntity(typeof(EcsIntElement)); + m_EntityB = EntityManager.CreateEntity(typeof(EcsIntElement)); + + m_BufferLookup = GetBufferLookup(); + } + + protected override void OnUpdate() + { + // This is a regression test for a subtle cache invalidation in component lookup. + + m_BufferLookup.Update(this); + + EntityManager.GetBuffer(m_EntityA).Add(123); + EntityManager.GetBuffer(m_EntityB).Add(234); + + // This first test of the contents of the buffers looks pointless, but it is + // actually quite relevant because using the lookup initializes the cache it contains. + + { + var arrayA = m_BufferLookup[m_EntityA].Reinterpret().AsNativeArray().ToArray(); + CollectionAssert.AreEqual(new[] { 123 }, arrayA); + + var arrayB = m_BufferLookup[m_EntityB].Reinterpret().AsNativeArray().ToArray(); + CollectionAssert.AreEqual(new[] { 234 }, arrayB); + } + + // The first two AddComponent below move the entities to a new chunk. + // Since the previous chunk is left empty, it gets deallocated. + + EntityManager.AddComponent(m_EntityA); + EntityManager.AddComponent(m_EntityB); + + // The next two AddComponent blow move the entities to a new chunk. + // But at this point, the previously deallocated chunk gets recycled. + // The entities are now back into their initial chunk, but with a different archetype. + + EntityManager.AddComponent(m_EntityA); + EntityManager.AddComponent(m_EntityB); + + m_BufferLookup.Update(this); + + EntityManager.GetBuffer(m_EntityA).Add(1234); + EntityManager.GetBuffer(m_EntityB).Add(2345); + + // Because the type indices of buffer components are always larger than the + // indices of regular components, the buffers are now located at a different + // offset in the chunk. + // If the cache in the lookup incorrectly assumes that the same chunk implies + // the same archetype, the cache contains an invalid offset. + + { + var arrayA = m_BufferLookup[m_EntityA].Reinterpret().AsNativeArray().ToArray(); + CollectionAssert.AreEqual(new[] { 123, 1234 }, arrayA); + + var arrayB = m_BufferLookup[m_EntityB].Reinterpret().AsNativeArray().ToArray(); + CollectionAssert.AreEqual(new[] { 234, 2345 }, arrayB); + } + } + } + + [Test] + public void BufferLookup_CacheRegressionTest() + { + var system = World.CreateSystemManaged(); + system.Update(); + } } } diff --git a/Unity.Entities.Tests/ConstrainedEntityCreationTests.cs b/Unity.Entities.Tests/ConstrainedEntityCreationTests.cs index 9ac53f8..56d602b 100644 --- a/Unity.Entities.Tests/ConstrainedEntityCreationTests.cs +++ b/Unity.Entities.Tests/ConstrainedEntityCreationTests.cs @@ -151,11 +151,10 @@ public void Instantiate_WithArchetypeNotMatchingQuery_Works() m_Manager.Instantiate(toInstantiate); m_Manager.Instantiate(toInstantiate, TmpNA(2)); m_Manager.Instantiate(toInstantiate, 2, World.UpdateAllocator.ToAllocator); - m_Manager.Instantiate(TmpNA(toInstantiate, toInstantiate), TmpNA(2)); } Assert.AreEqual(OriginalEntitiesCount, EcsTestDataEntitiesCount); - Assert.AreEqual(OriginalEntitiesCount * 7 + 1, Component01EntitiesCount); + Assert.AreEqual(OriginalEntitiesCount * 5 + 1, Component01EntitiesCount); } [Test] @@ -197,7 +196,6 @@ public void Instantiate_WithArchetypeMatchingQuery_Throws() Assert.Throws(() => m_Manager.Instantiate(toInstantiate)); Assert.Throws(() => m_Manager.Instantiate(toInstantiate, TmpNA(2))); Assert.Throws(() => m_Manager.Instantiate(toInstantiate, 2, World.UpdateAllocator.ToAllocator)); - Assert.Throws(() => m_Manager.Instantiate(TmpNA(toInstantiate, toInstantiate), TmpNA(2))); } Assert.AreEqual(OriginalEntitiesCount, EcsTestDataEntitiesCount); @@ -217,7 +215,6 @@ public void Instantiate_WhileUnregisteredJobIsScheduled_Throws() ScheduleJobAndAssertCodeThrows(query, typeHandle, () => m_Manager.Instantiate(toInstantiate)); ScheduleJobAndAssertCodeThrows(query, typeHandle, () => m_Manager.Instantiate(toInstantiate, TmpNA(2))); ScheduleJobAndAssertCodeThrows(query, typeHandle, () => m_Manager.Instantiate(toInstantiate, 2, World.UpdateAllocator.ToAllocator)); - ScheduleJobAndAssertCodeThrows(query, typeHandle, () => m_Manager.Instantiate(TmpNA(toInstantiate, toInstantiate), TmpNA(2))); Assert.AreEqual(OriginalEntitiesCount, EcsTestDataEntitiesCount); } @@ -236,10 +233,9 @@ public void Instantiate_WithArchetypeNotMatchingQuery_CompletesAllScheduledJobs( CreateSystemAndAssertAllScheduledJobsAreCompletedAfterRunningCode(query, ref typeHandle, () => m_Manager.Instantiate(toInstantiate)); CreateSystemAndAssertAllScheduledJobsAreCompletedAfterRunningCode(query, ref typeHandle, () => m_Manager.Instantiate(toInstantiate, TmpNA(2))); CreateSystemAndAssertAllScheduledJobsAreCompletedAfterRunningCode(query, ref typeHandle, () => m_Manager.Instantiate(toInstantiate, 2, World.UpdateAllocator.ToAllocator)); - CreateSystemAndAssertAllScheduledJobsAreCompletedAfterRunningCode(query, ref typeHandle, () => m_Manager.Instantiate(TmpNA(toInstantiate, toInstantiate), TmpNA(2))); Assert.AreEqual(OriginalEntitiesCount, EcsTestDataEntitiesCount); - Assert.AreEqual(OriginalEntitiesCount * 7 + 1, Component01EntitiesCount); + Assert.AreEqual(OriginalEntitiesCount * 5 + 1, Component01EntitiesCount); } [Test] @@ -261,22 +257,6 @@ public void CopyEntities_WithArchetypeNotMatchingQuery_Works() Assert.AreEqual(OriginalEntitiesCount * 2 + 1, Component01EntitiesCount); } - [Test] - public void CopyEntities_WithArchetypeNotMatchingQuery_CompletesAllScheduledJobs() - { - SetupEntitiesForConsistencyCheck(OriginalEntitiesCount); - - var toCopy = m_Manager.CreateEntity(typeof(Component01)); - - var typeHandle = new EcsTestDataAspect.TypeHandle(ref EmptySystem.CheckedStateRef); - var query = EmptySystem.GetEntityQuery(GetRequiredComponents()); - - CreateSystemAndAssertAllScheduledJobsAreCompletedAfterRunningCode(query, ref typeHandle, () => m_Manager.Instantiate(TmpNA(toCopy, toCopy), TmpNA(2))); - - Assert.AreEqual(OriginalEntitiesCount, EcsTestDataEntitiesCount); - Assert.AreEqual(OriginalEntitiesCount * 2 + 1, Component01EntitiesCount); - } - [Test] [TestRequiresDotsDebugOrCollectionChecks("Test requires entity query enumerator safety checks")] public void CopyEntities_WithArchetypeMatchingQuery_Throws() @@ -291,8 +271,7 @@ public void CopyEntities_WithArchetypeMatchingQuery_Throws() foreach (var aspect in EcsTestDataAspect.Query(query, typeHandle)) { AssertValueConsistency(aspect); - - Assert.Throws(() => m_Manager.Instantiate(TmpNA(toCopy, toCopy), TmpNA(2))); + Assert.Throws(() => m_Manager.Instantiate(toCopy)); } Assert.AreEqual(OriginalEntitiesCount, EcsTestDataEntitiesCount); @@ -401,7 +380,6 @@ public void Instantiate_Prefab_WithArchetypeMatchingQuery_Throws() Assert.Throws(() => m_Manager.Instantiate(prefabEntity)); Assert.Throws(() => m_Manager.Instantiate(prefabEntity, TmpNA(2))); Assert.Throws(() => m_Manager.Instantiate(prefabEntity, 2, World.UpdateAllocator.ToAllocator)); - Assert.Throws(() => m_Manager.Instantiate(TmpNA(prefabEntity, prefabEntity), TmpNA(2))); } } diff --git a/Unity.Entities.Tests/Content/RuntimeContentCatalogTests.cs b/Unity.Entities.Tests/Content/RuntimeContentCatalogTests.cs index 05057d8..c880c84 100644 --- a/Unity.Entities.Tests/Content/RuntimeContentCatalogTests.cs +++ b/Unity.Entities.Tests/Content/RuntimeContentCatalogTests.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System.Collections.Generic; using NUnit.Framework; using Unity.Collections; @@ -588,5 +587,3 @@ public void RuntimeContentCatalog_TryGetArchiveLocationHandle_FailsOnInvalidId() } } } - -#endif diff --git a/Unity.Entities.Tests/Content/RuntimeContentUtilityTests.cs b/Unity.Entities.Tests/Content/RuntimeContentUtilityTests.cs index 7003ce7..865975c 100644 --- a/Unity.Entities.Tests/Content/RuntimeContentUtilityTests.cs +++ b/Unity.Entities.Tests/Content/RuntimeContentUtilityTests.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Threading; using NUnit.Framework; @@ -113,4 +112,3 @@ public void MultiProducerSingleBulkConsumerQueueTests([Values(4, 8)] int produce } } } -#endif diff --git a/Unity.Entities.Tests/CreateAndDestroyTests.cs b/Unity.Entities.Tests/CreateAndDestroyTests.cs index a0e0d52..846f68e 100644 --- a/Unity.Entities.Tests/CreateAndDestroyTests.cs +++ b/Unity.Entities.Tests/CreateAndDestroyTests.cs @@ -54,8 +54,6 @@ unsafe public void CreateAndDestroyTwo() AssertDoesNotExist(entity1); } -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1432 [TestCaseGeneric(typeof(EcsTestData))] [TestCaseGeneric(typeof(EcsCleanup1))] unsafe public void CreateZeroEntities() @@ -66,8 +64,6 @@ unsafe public void CreateZeroEntities() array.Dispose(); } -#endif - [Test] unsafe public void InstantiateZeroEntities() { @@ -161,8 +157,6 @@ unsafe public void CreateAndDestroyThree() AssertComponentData(entity2, 12); } -#if !(UNITY_DOTSRUNTIME && (UNITY_WEBGL || UNITY_LINUX)) // https://unity3d.atlassian.net/browse/DOTSR-2039 for web - //https://unity3d.atlassian.net/browse/DOTSR-2653 for linux [Test] [Ignore("TODO(DOTS-5601): optimize performance and add checks that ensure memory consumption is in reasonable range")] public void CreateEntity_CreateAlmostTooManyEntities() @@ -173,7 +167,6 @@ public void CreateEntity_CreateAlmostTooManyEntities() Assert.IsTrue(almostTooManyEntities == (int) almostTooManyEntities); Assert.DoesNotThrow(() => m_Manager.CreateEntity(archetype, (int)almostTooManyEntities)); } -#endif [Test] [TestRequiresDotsDebugOrCollectionChecks("Test requires entity data access safety checks")] @@ -210,9 +203,7 @@ public void CreateEntity_NoArray_InvalidEntityArchetypeThrows() } [Test] -#if !UNITY_DOTSRUNTIME [ConditionalIgnore("IgnoreForCoverage", "Fails randonly when ran with code coverage enabled")] -#endif unsafe public void CreateAndDestroyStressTest() { var archetype = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2)); @@ -316,12 +307,6 @@ public void AddRemoveComponent_FailForInt() var archetype = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2)); var entity = m_Manager.CreateEntity(archetype); -#if UNITY_DOTSRUNTIME - Assert.Throws(() => m_Manager.AddComponent(entity)); - Assert.Throws(() => m_Manager.RemoveComponent(entity)); - Assert.Throws(() => m_Manager.AddComponent(entity, typeof(int))); - Assert.Throws(() => m_Manager.RemoveComponent(entity, typeof(int))); -#else Assert.That(() => { m_Manager.AddComponent(entity); }, Throws.ArgumentException.With.Message.Contains("All ComponentType must be known at compile time.")); Assert.That(() => { m_Manager.RemoveComponent(entity); }, @@ -330,7 +315,6 @@ public void AddRemoveComponent_FailForInt() Throws.ArgumentException.With.Message.Contains("All ComponentType must be known at compile time.")); Assert.That(() => { m_Manager.RemoveComponent(entity, typeof(int)); }, Throws.ArgumentException.With.Message.Contains("All ComponentType must be known at compile time.")); -#endif } [Test] @@ -339,12 +323,6 @@ public void AddRemoveComponent_FailForNonIComponentData() var archetype = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2)); var entity = m_Manager.CreateEntity(archetype); -#if UNITY_DOTSRUNTIME - Assert.Throws(() => m_Manager.AddComponent(entity)); - Assert.Throws(() => m_Manager.RemoveComponent(entity)); - Assert.Throws(() => m_Manager.AddComponent(entity, typeof(EcsTestNonComponent))); - Assert.Throws(() => m_Manager.RemoveComponent(entity, typeof(EcsTestNonComponent))); -#else Assert.That(() => { m_Manager.AddComponent(entity); }, Throws.ArgumentException.With.Message.Contains("All ComponentType must be known at compile time.")); Assert.That(() => { m_Manager.RemoveComponent(entity); }, @@ -353,7 +331,6 @@ public void AddRemoveComponent_FailForNonIComponentData() Throws.ArgumentException.With.Message.Contains("All ComponentType must be known at compile time.")); Assert.That(() => { m_Manager.RemoveComponent(entity, typeof(EcsTestNonComponent)); }, Throws.ArgumentException.With.Message.Contains("All ComponentType must be known at compile time.")); -#endif } [Test] @@ -710,10 +687,6 @@ public void AddComponent_WithTypeIndices_Works() actualTotalTypes.Dispose(); } -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1432 - // TODO: IL2CPP_TEST_RUNNER can't handle Assert.That Throws - [Test] [TestRequiresDotsDebugOrCollectionChecks("Test requires entity data access safety checks")] public void AddComponent_InvalidEntity1_ShouldThrow() @@ -749,9 +722,8 @@ public void AddComponentBatched_InvalidEntities_ShouldThrow([Values(10, 100)] in entities[0] = invalidEnt; Assert.That(() => { m_Manager.AddComponent(entities); }, - Throws.ArgumentException.With.Message.Contains("All entities passed to EntityManager must exist")); + Throws.ArgumentException.With.Message.Contains("An EntityManager command is operating on an invalid entity.")); } -#endif [Test] public void AddComponent_WithSharedComponents_Works() @@ -945,16 +917,76 @@ public void AddChunkComponentTwice() [Test] [TestRequiresDotsDebugOrCollectionChecks("Test requires entity data access safety checks")] - public void AddChunkComponentToGroupTwice() - { - m_Manager.CreateEntity(); - - m_Manager.AddChunkComponentData(m_Manager.UniversalQuery, new EcsTestTag()); - Assert.Throws(() => m_Manager.AddChunkComponentData(m_Manager.UniversalQuery, new EcsTestTag())); + public void AddChunkComponent_Works() + { + var entity = m_Manager.CreateEntity(typeof(EcsTestData2)); + + // Adding a chunk component through an entity should be visible through both ArchetypeChunk and EntityManager interfaces + Assert.IsTrue(m_Manager.AddChunkComponentData(entity)); + Assert.IsTrue(m_Manager.HasChunkComponent(entity)); + Assert.IsTrue(m_Manager.GetChunk(entity).HasChunkComponent()); + // Setting the chunk component value through EntityManager should be visible through both chunk and EntityManager interfaces + m_Manager.SetChunkComponentData(m_Manager.GetChunk(entity), new EcsTestData { value = 17 }); + Assert.AreEqual(17, m_Manager.GetChunkComponentData(entity).value); + Assert.AreEqual(17, m_Manager.GetChunkComponentData(m_Manager.GetChunk(entity)).value); + var typeHandle = m_Manager.GetComponentTypeHandle(false); + Assert.AreEqual(17, m_Manager.GetChunk(entity).GetChunkComponentData(ref typeHandle).value); + + // Adding a second chunk component through a query should be visible through both the ArchetypeChunk and EntityManager interfaces + var chunk2 = m_Manager.GetChunk(entity); + using var query = new EntityQueryBuilder(Allocator.Temp).WithAll().Build(m_Manager); + m_Manager.AddChunkComponentData(query, new EcsTestDataEnableable{value=23}); + Assert.IsTrue(m_Manager.HasChunkComponent(entity)); + Assert.IsTrue(m_Manager.GetChunk(entity).HasChunkComponent()); + Assert.AreEqual(23, m_Manager.GetChunkComponentData(entity).value); + Assert.AreEqual(23, m_Manager.GetChunkComponentData(m_Manager.GetChunk(entity)).value); + var typeHandle2 = m_Manager.GetComponentTypeHandle(false); + Assert.AreEqual(23, m_Manager.GetChunk(entity).GetChunkComponentData(ref typeHandle2).value); + + // The first chunk component is still present, and its value is preserved in this case, because the adding + // the second chunk component didn't change the entity's chunk. + Assert.AreEqual(chunk2.m_Chunk, m_Manager.GetChunk(entity).m_Chunk); + Assert.IsTrue(m_Manager.HasChunkComponent(entity)); + Assert.IsTrue(m_Manager.GetChunk(entity).HasChunkComponent()); + Assert.AreEqual(17, m_Manager.GetChunkComponentData(entity).value); + Assert.AreEqual(17, m_Manager.GetChunkComponentData(m_Manager.GetChunk(entity)).value); + var typeHandle3 = m_Manager.GetComponentTypeHandle(false); // typeHandle was invalidated by the AddChunkComponentData call + Assert.AreEqual(17, m_Manager.GetChunk(entity).GetChunkComponentData(ref typeHandle3).value); + } - m_Manager.AddChunkComponentData(m_Manager.UniversalQuery, new EcsTestData {value = 123}); - Assert.Throws(() => m_Manager.AddChunkComponentData(m_Manager.UniversalQuery, new EcsTestData {value = 123})); - Assert.Throws(() => m_Manager.AddChunkComponentData(m_Manager.UniversalQuery, new EcsTestData {value = 456})); + [Test] + [TestRequiresDotsDebugOrCollectionChecks("Test requires entity data access safety checks")] + public void AddChunkComponent_AlreadyExists_Works() + { + var entity = m_Manager.CreateEntity(typeof(EcsTestData2)); + using var query = new EntityQueryBuilder(Allocator.Temp).WithAll().Build(m_Manager); + + m_Manager.AddComponent(query); + // Adding a tag component that already exists is not an error + Assert.DoesNotThrow(() => m_Manager.AddComponent(query)); + + m_Manager.AddComponentData(entity, new EcsTestData{value = 17}); + // Adding a component that already exists is not an error + Assert.DoesNotThrow(() => m_Manager.AddComponent(query)); + // Adding a component value when the component already exists is not an error; it just acts like SetComponentData + Assert.DoesNotThrow(() => m_Manager.AddComponentData(entity, new EcsTestData { value = 23 })); + Assert.AreEqual(23, m_Manager.GetComponentData(entity).value); + + // ...same for shared components. + m_Manager.AddSharedComponent(query, new EcsTestSharedComp{value=23}); + // Adding a shared component value when the component already exists is not an error; it just acts like SetSharedComponentData + Assert.DoesNotThrow(() => m_Manager.AddSharedComponent(query, new EcsTestSharedComp{value=37})); + Assert.AreEqual(37, m_Manager.GetSharedComponent(entity).value); + + // Everything above should be true for chunk components as well. + m_Manager.AddChunkComponentData(entity); + // Adding a tag chunk component that already exists is not an error + Assert.DoesNotThrow(() => m_Manager.AddChunkComponentData(entity)); + + m_Manager.AddChunkComponentData(query, new EcsTestFloatData {Value = 123}); + // Adding a chunk component value when the component already exists is not an error; it just acts like SetChunkComponentData + Assert.DoesNotThrow(() => m_Manager.AddChunkComponentData(query, new EcsTestFloatData {Value = 456})); + Assert.AreEqual(456, m_Manager.GetChunkComponentData(entity).Value); } [Test] diff --git a/Unity.Entities.Tests/Diff/CopyAndReplaceTests.cs b/Unity.Entities.Tests/Diff/CopyAndReplaceTests.cs index 4b90691..b473c1c 100644 --- a/Unity.Entities.Tests/Diff/CopyAndReplaceTests.cs +++ b/Unity.Entities.Tests/Diff/CopyAndReplaceTests.cs @@ -18,7 +18,7 @@ unsafe void CreateTestData(out Entity entity, out Entity metaEntity, int value, SrcEntityManager.AddSharedComponent(entity, new EcsTestSharedComp(6)); SrcEntityManager.AddChunkComponentData(SrcEntityManager.UniversalQuery, new EcsTestData2(7)); - metaEntity = SrcEntityManager.GetChunk(entity).m_Chunk->metaChunkEntity; + metaEntity = SrcEntityManager.GetChunk(entity).m_Chunk.MetaChunkEntity; Assert.AreEqual(7, SrcEntityManager.GetComponentData(metaEntity).value0); } @@ -29,7 +29,7 @@ unsafe void TestValues(Entity entity, Entity metaEntity, int componentDataValue Assert.AreEqual(6, DstEntityManager.GetSharedComponent(entity).value); Assert.AreEqual(componentChunkValue, DstEntityManager.GetChunkComponentData(entity).value0); - Assert.AreEqual(metaEntity, DstEntityManager.GetChunk(entity).m_Chunk->metaChunkEntity); + Assert.AreEqual(metaEntity, DstEntityManager.GetChunk(entity).m_Chunk.MetaChunkEntity); SrcEntityManager.Debug.CheckInternalConsistency(); DstEntityManager.Debug.CheckInternalConsistency(); diff --git a/Unity.Entities.Tests/Diff/CopyEntitiesFromTests.cs b/Unity.Entities.Tests/Diff/CopyEntitiesFromTests.cs index 760a407..a5388e2 100644 --- a/Unity.Entities.Tests/Diff/CopyEntitiesFromTests.cs +++ b/Unity.Entities.Tests/Diff/CopyEntitiesFromTests.cs @@ -21,7 +21,6 @@ void TestValues(Entity entity, int componentDataValue, int componentChunkValue) } #if !UNITY_DISABLE_MANAGED_COMPONENTS - [DotsRuntimeFixme] [Test] public void CopyEntitiesToOtherWorld() { diff --git a/Unity.Entities.Tests/Diff/EntityDifferTests.cs b/Unity.Entities.Tests/Diff/EntityDifferTests.cs index 9d5b616..12f45a7 100644 --- a/Unity.Entities.Tests/Diff/EntityDifferTests.cs +++ b/Unity.Entities.Tests/Diff/EntityDifferTests.cs @@ -11,10 +11,6 @@ namespace Unity.Entities.Tests [TestFixture] sealed class EntityDifferTests : EntityDifferTestFixture { -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1435 - // These tests cause crashes in the IL2CPP runner. Cause not yet debugged. - [Test] public void EntityDiffer_GetChanges_NoChanges() { @@ -547,7 +543,6 @@ public void EntityDiffer_GetChanges_CreateEntityAndSetEnableableBuffer_Increment } [Test] - [DotsRuntimeFixme("Do not support EntityNames - DOTS-3862")] public void EntityDifferDetectsNameChanges() { using (var differ = new EntityManagerDiffer(SrcEntityManager, SrcWorld.UpdateAllocator.ToAllocator)) @@ -1433,7 +1428,6 @@ public void SetComponentData_WithManagedComponent_IsDetectedBy_GetChanges() } [Test] - [DotsRuntimeFixme] // Requires Unity.Properties support for cloning the managed components public void GetComponentData_WithManagedComponent_IsDetectedBy_GetChanges() { using (var differ = new EntityManagerDiffer(SrcEntityManager, SrcWorld.UpdateAllocator.ToAllocator)) @@ -1800,7 +1794,6 @@ public void EntityDiffer_GetChanges_BlobAssets_SetBuffer_MultipleEntities() // this requires the magic values to be updated in this test every time. This test needs some re-thinking. /* [Test] - [DotsRuntimeFixme] public unsafe void EntityDiffer_GetChanges_BlobAssets_SetComponent_TypeMemoryOrdering() { using (var differ = new EntityManagerDiffer(SrcEntityManager, SrcWorld.UpdateAllocator.ToAllocator)) @@ -1870,7 +1863,6 @@ struct SharedComponentWithBlobAssetRef : ISharedComponentData } [Test] - [DotsRuntimeFixme] // Requires Unity.Properties support #if ENABLE_IL2CPP [Ignore("DOTS-7524 - \"System.ExecutionEngineException : An unresolved indirect call lookup failed\" is thrown when executed with an IL2CPP build")] #endif @@ -1932,7 +1924,6 @@ public unsafe void EntityDiffer_GetChanges_BlobAssets_ManagedComponents() } [Test] - [DotsRuntimeFixme] // Requires Unity.Properties support #if ENABLE_IL2CPP [Ignore("DOTS-7524 - \"System.ExecutionEngineException : An unresolved indirect call lookup failed\" is thrown when executed with an IL2CPP build")] #endif @@ -1997,7 +1988,6 @@ public unsafe void EntityDiffer_GetChanges_BlobAssets_SharedComponents() } } -#if !UNITY_DOTSRUNTIME class ManagedComponentWithScriptableObject: IComponentData { public ScriptableObjectWithBlobAssetRef Value; @@ -2040,8 +2030,6 @@ public void EntityDiffer_GetChanges_BlobAssets_ScriptableObject() UnityEngine.Object.DestroyImmediate(scriptableObject); } } -#endif // !UNITY_DOTSRUNTIME - #endif // !UNITY_DISABLE_MANAGED_COMPONENTS [Test] @@ -2145,6 +2133,5 @@ public void EntityDiffer_GatherLinkedEntityGroupChanges_DetectsCombinedChanges() Assert.AreEqual(c, removals[0].ChildEntityGuid); } } -#endif // !UNITY_DOTSRUNTIME_IL2CPP } } diff --git a/Unity.Entities.Tests/Diff/EntityGuidTests.cs b/Unity.Entities.Tests/Diff/EntityGuidTests.cs index 1a4029b..c3a62b9 100644 --- a/Unity.Entities.Tests/Diff/EntityGuidTests.cs +++ b/Unity.Entities.Tests/Diff/EntityGuidTests.cs @@ -1,7 +1,5 @@ using NUnit.Framework; -#if !NET_DOTS using System.Linq; -#endif namespace Unity.Entities.Tests { @@ -32,8 +30,6 @@ public void ToString_ExtractsPackedValues() Assert.That(g1.ToString(), Is.EqualTo("-1:0:000000f0:89abcdef")); } -#if !NET_DOTS - // https://unity3d.atlassian.net/browse/DOTSR-1432 [Test] public void Comparisons() { @@ -61,7 +57,5 @@ public void Comparisons() Assert.That(hashCodeEquals, Is.EqualTo(new[] { true, false, false, false, false })); Assert.That(compareTo, Is.EqualTo(new[] { 0, 1, 1, -1, 1 })); } - -#endif } } diff --git a/Unity.Entities.Tests/Diff/EntityPatcherTests.cs b/Unity.Entities.Tests/Diff/EntityPatcherTests.cs index 02212a6..53e4040 100644 --- a/Unity.Entities.Tests/Diff/EntityPatcherTests.cs +++ b/Unity.Entities.Tests/Diff/EntityPatcherTests.cs @@ -8,9 +8,6 @@ namespace Unity.Entities.Tests [TestFixture] sealed class EntityPatcherTests : EntityDifferTestFixture { -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1435 - // These tests cause crashes in the IL2CPP runner. Cause not yet debugged. [Test] public void EntityPatcher_ApplyChanges_NoChanges() { @@ -266,7 +263,6 @@ public unsafe void EntityPatcher_ApplyChanges_CreateSharedComponent() } } -#if !UNITY_DOTSRUNTIME // Related to shared components [Test] public void EntityPatcher_ApplyChanges_ChangeSharedComponent() { @@ -297,8 +293,6 @@ public void EntityPatcher_ApplyChanges_ChangeSharedComponent() } } -#endif - [Test] public void EntityPatcher_ApplyChanges_ChangeAppliesToAllPrefabInstances([Values] bool prefabTag) { @@ -578,107 +572,7 @@ public void EntityPatcher_ApplyChanges_NewEntityIsReplicatedIntoExistingPrefabIn } } - [Test] - public void EntityPatcher_ApplyChanges_WithChunkData() - { - using (var differ = new EntityManagerDiffer(SrcEntityManager, SrcWorld.UpdateAllocator.ToAllocator)) - { - var guid = CreateEntityGuid(); - var entity = SrcEntityManager.CreateEntity(); - Entity dstRootEntity; - // Chunk component is added but no values are copied - // Because chunks are generally caches and thus must be rebuildable automatically. - // They are also likely a totally different set of chunks. - // Diff & Patch is generally working against entities not on chunk level - { - SrcEntityManager.AddComponentData(entity, guid); - SrcEntityManager.AddComponentData(entity, new EcsTestData(1)); - SrcEntityManager.AddChunkComponentData(entity); - SrcEntityManager.SetChunkComponentData(SrcEntityManager.GetChunk(entity), new EcsTestData2(3)); - - PushChanges(differ, DstEntityManager, DstWorld.UpdateAllocator.ToAllocator); - - dstRootEntity = GetEntity(DstEntityManager, guid); - Assert.AreEqual(1, DstEntityManager.GetComponentData(dstRootEntity).value); - Assert.IsTrue(DstEntityManager.HasChunkComponent(dstRootEntity)); - Assert.AreEqual(0, DstEntityManager.GetChunkComponentData(dstRootEntity).value0); - Assert.AreEqual(1, DstEntityManager.CreateEntityQuery(typeof(ChunkHeader)).CalculateEntityCount()); - } - - // Changing Chunk component creates no diff - { - SrcEntityManager.SetChunkComponentData(SrcEntityManager.GetChunk(entity), new EcsTestData2(7)); - using (var changes = differ.GetChanges(EntityManagerDifferOptions.Default, SrcWorld.UpdateAllocator.ToAllocator)) - { - Assert.IsFalse(changes.AnyChanges); - } - } - - // Removing chunk component, removes chunk component again - { - SrcEntityManager.RemoveChunkComponent(entity); - PushChanges(differ, DstEntityManager, DstWorld.UpdateAllocator.ToAllocator); - Assert.IsFalse(DstEntityManager.HasChunkComponent(dstRootEntity)); - Assert.AreEqual(0, DstEntityManager.CreateEntityQuery(typeof(ChunkHeader)).CalculateEntityCount()); - } - } - } - #if !UNITY_DISABLE_MANAGED_COMPONENTS - [Test] - [IgnoreTest_IL2CPP("DOTSE-1903 - Users Properties which is broken in non-generic sharing IL2CPP builds")] - public void EntityPatcher_ApplyChanges_WithChunkData_ManagedComponents() - { - using (var differ = new EntityManagerDiffer(SrcEntityManager, SrcWorld.UpdateAllocator.ToAllocator)) - { - var guid = CreateEntityGuid(); - var entity = SrcEntityManager.CreateEntity(); - Entity dstRootEntity; - // Chunk component is added but no values are copied - // Because chunks are generally caches and thus must be rebuildable automatically. - // They are also likely a totally different set of chunks. - // Diff & Patch is generally working against entities not on chunk level - { - SrcEntityManager.AddComponentData(entity, guid); - SrcEntityManager.AddComponentData(entity, new EcsTestData(1)); - SrcEntityManager.AddChunkComponentData(entity); - SrcEntityManager.SetChunkComponentData(SrcEntityManager.GetChunk(entity), new EcsTestData2(3)); - SrcEntityManager.AddChunkComponentData(entity); - SrcEntityManager.SetChunkComponentData(SrcEntityManager.GetChunk(entity), new EcsTestManagedComponent() { value = "SomeString" }); - - PushChanges(differ, DstEntityManager, DstWorld.UpdateAllocator.ToAllocator); - - dstRootEntity = GetEntity(DstEntityManager, guid); - Assert.AreEqual(1, DstEntityManager.GetComponentData(dstRootEntity).value); - Assert.IsTrue(DstEntityManager.HasChunkComponent(dstRootEntity)); - Assert.IsTrue(DstEntityManager.HasChunkComponent(dstRootEntity)); - Assert.AreEqual(0, DstEntityManager.GetChunkComponentData(dstRootEntity).value0); - Assert.AreEqual(null, DstEntityManager.GetChunkComponentData(dstRootEntity)); - Assert.AreEqual(1, DstEntityManager.CreateEntityQuery(typeof(ChunkHeader)).CalculateEntityCount()); - } - - // Changing Chunk component creates no diff - { - SrcEntityManager.SetChunkComponentData(SrcEntityManager.GetChunk(entity), new EcsTestData2(7)); - SrcEntityManager.SetChunkComponentData(SrcEntityManager.GetChunk(entity), new EcsTestManagedComponent() { value = "SomeOtherString" }); - using (var changes = differ.GetChanges(EntityManagerDifferOptions.Default, SrcWorld.UpdateAllocator.ToAllocator)) - { - Assert.IsFalse(changes.AnyChanges); - } - } - - // Removing chunk component, removes chunk component again - { - SrcEntityManager.RemoveChunkComponent(entity); - SrcEntityManager.RemoveChunkComponent(entity); - PushChanges(differ, DstEntityManager, DstWorld.UpdateAllocator.ToAllocator); - Assert.IsFalse(DstEntityManager.HasChunkComponent(dstRootEntity)); - Assert.IsFalse(DstEntityManager.HasChunkComponent(dstRootEntity)); - Assert.AreEqual(0, DstEntityManager.CreateEntityQuery(typeof(ChunkHeader)).CalculateEntityCount()); - } - } - } - [Test] [IgnoreTest_IL2CPP("DOTSE-1903 - Users Properties which is broken in non-generic sharing IL2CPP builds")] public void EntityPatcher_ApplyChanges_CreateEntityWithTestData_ManagedComponents() @@ -722,7 +616,6 @@ public void EntityPatcher_ApplyChanges_CreateEntityWithTestData_ManagedComponent } [Test] - [DotsRuntimeFixme] // Requires Unity.Properties [IgnoreTest_IL2CPP("DOTSE-1903 - Users Properties which is broken in non-generic sharing IL2CPP builds")] public void EntityPatcher_ApplyChanges_RemapEntityReferencesInManagedComponents() { @@ -754,7 +647,6 @@ public void EntityPatcher_ApplyChanges_RemapEntityReferencesInManagedComponents( // https://unity3d.atlassian.net/browse/DOTSR-1432 [Test] - [DotsRuntimeFixme] // No support for PinGCObject [IgnoreTest_IL2CPP("DOTSE-1903 - Users Properties which is broken in non-generic sharing IL2CPP builds")] public void EntityPatcher_ApplyChanges_RemapEntityReferencesInManagedComponentCollection() { @@ -1201,7 +1093,5 @@ public void EntityPatcher_ApplyChanges_LinkedEntityGroups_CombineTwoGroups() } } } - -#endif // !UNITY_PORTABLE_TEST_RUNNER } } diff --git a/Unity.Entities.Tests/Diff/HierarchyIntegrationTest.cs b/Unity.Entities.Tests/Diff/HierarchyIntegrationTest.cs index 3e8ae65..20f281d 100644 --- a/Unity.Entities.Tests/Diff/HierarchyIntegrationTest.cs +++ b/Unity.Entities.Tests/Diff/HierarchyIntegrationTest.cs @@ -3,10 +3,6 @@ namespace Unity.Entities.Tests { -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1435 - // These tests cause crashes in the IL2CPP runner. Cause not yet debugged. - [TestFixture] internal sealed class HierarchyIntegrationTest : EntityDifferTestFixture { @@ -159,5 +155,4 @@ public void UndoRedoPrefabInstancesWithRelationship() } } } -#endif } diff --git a/Unity.Entities.Tests/DotsRuntimeTestAttributes.cs b/Unity.Entities.Tests/DotsRuntimeTestAttributes.cs deleted file mode 100644 index 3cca76f..0000000 --- a/Unity.Entities.Tests/DotsRuntimeTestAttributes.cs +++ /dev/null @@ -1,64 +0,0 @@ -using System; -using NUnit.Framework; - -namespace Unity.Entities.Tests -{ -#if UNITY_DOTSRUNTIME - public class DotsRuntimeFixmeAttribute : IgnoreAttribute - { - public DotsRuntimeFixmeAttribute(string msg = null) : base(msg == null ? "Test should work in DOTS Runtime but currently doesn't. Ignoring until fixed..." : msg) - { - } - } -#else - public class DotsRuntimeFixmeAttribute : Attribute - { - public DotsRuntimeFixmeAttribute(string msg = null) - { - } - } -#endif - -#if UNITY_DOTSRUNTIME - public class DotsRuntimeIncompatibleTestAttribute : IgnoreAttribute - { - public DotsRuntimeIncompatibleTestAttribute(string reason) : base(reason) - { - } - } -#else - public class DotsRuntimeIncompatibleTestAttribute : Attribute - { - public DotsRuntimeIncompatibleTestAttribute(string reason) - { - } - } -#endif - -#if UNITY_PORTABLE_TEST_RUNNER - class IgnoreInPortableTests : IgnoreAttribute - { - public IgnoreInPortableTests(string reason) : base(reason) - { - } - } - - class ManagedExceptionInPortableTests : IgnoreAttribute - { - public ManagedExceptionInPortableTests() - : base("Test uses managed exceptions, which are unsupported in DOTS-Runtime.") { } - } - -#else - internal class IgnoreInPortableTestsAttribute : Attribute - { - public IgnoreInPortableTestsAttribute(string reason) - { - } - } - - class ManagedExceptionInPortableTestsAttribute : Attribute - { - } -#endif -} diff --git a/Unity.Entities.Tests/DotsRuntimeTestAttributes.cs.meta b/Unity.Entities.Tests/DotsRuntimeTestAttributes.cs.meta deleted file mode 100644 index 51cfce5..0000000 --- a/Unity.Entities.Tests/DotsRuntimeTestAttributes.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 16ea6314230823d4e89475573000b8e2 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities.Tests/DynamicBufferTests.cs b/Unity.Entities.Tests/DynamicBufferTests.cs index 26fae75..430bdd4 100644 --- a/Unity.Entities.Tests/DynamicBufferTests.cs +++ b/Unity.Entities.Tests/DynamicBufferTests.cs @@ -11,9 +11,7 @@ using Unity.Collections.LowLevel.Unsafe; using UnityEngine; using UnityEngine.TestTools; -#if !UNITY_DOTSRUNTIME using System.Text.RegularExpressions; -#endif namespace Unity.Entities.Tests { @@ -222,7 +220,7 @@ public void DynamicBuffer_ElementWithContainer_Works() element.Destroy(); } - [Test, DotsRuntimeFixme("Job system update required to support nested container safety")] + [Test] [TestRequiresCollectionChecks("Relies on jobs debugger")] public void DynamicBuffer_ElementWithContainerInJob_Throws() { @@ -366,7 +364,6 @@ public void ReadOnlyBufferDoesNotBumpVersionNumber_BufferLookup() Assert.AreEqual(0, buffer.Length); } -#if !UNITY_DOTSRUNTIME // DOTS Runtime does not support regex [Test] [TestRequiresCollectionChecks("Requires Job Safety System")] public void WritingToReadOnlyBufferTriggersSafetySystem() @@ -464,9 +461,6 @@ public void DynamicBufferAliasing() { World.GetOrCreateSystemManaged().Update(); } -#endif - -#if !UNITY_DOTSRUNTIME // No GCAlloc access // @TODO: when 2019.1 support is dropped this can be shared with the collections tests: // until then the package validation will fail otherwise when collections is not marked testable @@ -525,8 +519,5 @@ public void DynamicBufferForEach() Assert.AreEqual(2, count); Assert.AreEqual(8, sum); } - - -#endif } } diff --git a/Unity.Entities.Tests/ECSTestsFixture.cs b/Unity.Entities.Tests/ECSTestsFixture.cs index 72a24e7..350f0dc 100644 --- a/Unity.Entities.Tests/ECSTestsFixture.cs +++ b/Unity.Entities.Tests/ECSTestsFixture.cs @@ -4,9 +4,7 @@ using Unity.Jobs.LowLevel.Unsafe; using Assert = FastAssert; using Unity.Burst; -#if !UNITY_DOTSRUNTIME using UnityEngine.LowLevel; -#endif namespace Unity.Entities.Tests { @@ -80,18 +78,11 @@ public class ECSTestsCommonBase [SetUp] public virtual void Setup() { -#if UNITY_DOTSRUNTIME - Unity.Runtime.TempMemoryScope.EnterScope(); - UnityEngine.TestTools.LogAssert.ExpectReset(); -#endif } [TearDown] public virtual void TearDown() { -#if UNITY_DOTSRUNTIME - Unity.Runtime.TempMemoryScope.ExitScope(); -#endif } [BurstDiscard] @@ -114,9 +105,7 @@ public abstract class ECSTestsFixture : ECSTestsCommonBase { protected World m_PreviousWorld; protected World World; -#if !UNITY_DOTSRUNTIME protected PlayerLoopSystem m_PreviousPlayerLoop; -#endif protected EntityManager m_Manager; protected EntityManager.EntityManagerDebug m_ManagerDebug; @@ -128,11 +117,9 @@ public override void Setup() { base.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"); @@ -145,9 +132,7 @@ public override void Setup() JobsDebuggerWasEnabled = JobsUtility.JobDebuggerEnabled; JobsUtility.JobDebuggerEnabled = true; -#if !UNITY_DOTSRUNTIME JobsUtility.ClearSystemIds(); -#endif #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING // In case entities journaling is initialized, clear it @@ -178,14 +163,9 @@ public override void TearDown() } JobsUtility.JobDebuggerEnabled = JobsDebuggerWasEnabled; - -#if !UNITY_DOTSRUNTIME JobsUtility.ClearSystemIds(); -#endif -#if !UNITY_DOTSRUNTIME PlayerLoop.SetPlayerLoop(m_PreviousPlayerLoop); -#endif base.TearDown(); } diff --git a/Unity.Entities.Tests/EmbeddedPackageOnlyTestAttribute.cs b/Unity.Entities.Tests/EmbeddedPackageOnlyTestAttribute.cs index 404296a..94f20c2 100644 --- a/Unity.Entities.Tests/EmbeddedPackageOnlyTestAttribute.cs +++ b/Unity.Entities.Tests/EmbeddedPackageOnlyTestAttribute.cs @@ -1,18 +1,10 @@ -using System; +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 { @@ -43,5 +35,4 @@ public void ApplyToTest(Test test) test.Properties.Add(PropertyNames.SkipReason, "Only runs in the editor when this package is embedded or referenced locally."); } } -#endif } diff --git a/Unity.Entities.Tests/EntitiesAssert/DebugEntity.cs b/Unity.Entities.Tests/EntitiesAssert/DebugEntity.cs index f113f92..6108eec 100644 --- a/Unity.Entities.Tests/EntitiesAssert/DebugEntity.cs +++ b/Unity.Entities.Tests/EntitiesAssert/DebugEntity.cs @@ -1,5 +1,3 @@ -#if !NET_DOTS -// https://unity3d.atlassian.net/browse/DOTSR-1432 using System; using System.Collections.Generic; using System.Diagnostics; @@ -161,10 +159,8 @@ public string ToString(int maxDataLen) if (Data is object[] objects) str += $"=len:{objects.Length}"; - #if !UNITY_DOTSRUNTIME else if (Data is UnityEngine.Component component) str += $"={component.gameObject.name}"; - #endif else if (!dataType.IsValueType || !Equals(Data, Activator.CreateInstance(dataType))) { var dataStr = Data.ToString(); @@ -226,4 +222,3 @@ public static bool TryGetComponent(this DebugEntity de, out T component) } } } -#endif diff --git a/Unity.Entities.Tests/EntitiesAssert/DebugEntityTests.cs b/Unity.Entities.Tests/EntitiesAssert/DebugEntityTests.cs index 40a0d39..ee98e7a 100644 --- a/Unity.Entities.Tests/EntitiesAssert/DebugEntityTests.cs +++ b/Unity.Entities.Tests/EntitiesAssert/DebugEntityTests.cs @@ -1,10 +1,6 @@ using System; using NUnit.Framework; -#if !NET_DOTS -// https://unity3d.atlassian.net/browse/DOTSR-1432 -// EntitiesAssert aren't currently supported. - namespace Unity.Entities.Tests { public class DebugEntityTests : ECSTestsFixture @@ -43,10 +39,6 @@ public void GetAllEntities_WithTaggedEntity() debugEntities); } -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1432 - // TODO: IL2CPP_TEST_RUNNER can't handle Is.Instance() and With() chains - [Test] public void GetAllEntities_WithSharedTagEntity() { @@ -54,28 +46,13 @@ public void GetAllEntities_WithSharedTagEntity() var debugEntities = DebugEntity.GetAllEntitiesWithSystems(m_Manager); -#if NET_DOTS - - // until ManagedComponentStore.GetSharedComponentDataBoxed supports an alternative to Activator to construct - // a default instance of T, we can't support it here. once implemented, remove this special case to the test - // and drop the try/catch from DebugComponent ctor. - Assert.That( - debugEntities[0].Components[0].Data, - Is.InstanceOf().With.Message.Match("Implement TypeManager.*DefaultValue")); - -#else - EntitiesAssert.AreEqual( new[] { new DebugEntity(entity, new DebugComponent {Type = typeof(Simulate), Data = new Simulate()}, new DebugComponent { Type = typeof(EcsTestSharedTag), Data = new EcsTestSharedTag() }) }, debugEntities); - -#endif } -#endif - [Test] public void GetAllEntities_WithComponentData() { @@ -136,7 +113,6 @@ public void GetAllEntities_WithBufferElementData() debugEntities); } -#if !UNITY_DOTSRUNTIME class TestClassComponent : UnityEngine.Object { public int Value; @@ -172,8 +148,6 @@ public void GetAllEntities_WithComponentObject() new DebugComponent {Type = typeof(Simulate), Data = new Simulate()}) }, debugEntities); } - -#endif // !UNITY_DOTSRUNTIME } public class DebugComponentTests @@ -217,5 +191,3 @@ public void ToString_WithGreaterOrEqualOrDefaultMaxLen_DoesNotTruncate() } } } - -#endif // !NET_DOTS diff --git a/Unity.Entities.Tests/EntitiesAssert/EntitiesAssert.cs b/Unity.Entities.Tests/EntitiesAssert/EntitiesAssert.cs index 89d1c2f..46a2ba5 100644 --- a/Unity.Entities.Tests/EntitiesAssert/EntitiesAssert.cs +++ b/Unity.Entities.Tests/EntitiesAssert/EntitiesAssert.cs @@ -1,6 +1,3 @@ -#if !NET_DOTS -// https://unity3d.atlassian.net/browse/DOTSR-1432 - using System; using System.Collections; using System.Collections.Generic; @@ -348,4 +345,3 @@ string MakeDesc() } } } -#endif // NET_DOTS diff --git a/Unity.Entities.Tests/EntitiesAssert/EntitiesAssertTests.cs b/Unity.Entities.Tests/EntitiesAssert/EntitiesAssertTests.cs index 5c33ccc..aa7e971 100644 --- a/Unity.Entities.Tests/EntitiesAssert/EntitiesAssertTests.cs +++ b/Unity.Entities.Tests/EntitiesAssert/EntitiesAssertTests.cs @@ -1,7 +1,3 @@ -#if !UNITY_PORTABLE_TEST_RUNNER -// https://unity3d.atlassian.net/browse/DOTSR-1432 -// The main problem is that EntitiesAssert uses LINQ - using System; using System.Collections.Generic; using NUnit.Framework; @@ -262,5 +258,3 @@ public void ContainsOnly_WithCustomMatcher_OnlyCalledOnce() } } } - -#endif // NET_DOTS diff --git a/Unity.Entities.Tests/EntityArchetypeQueryTests.cs b/Unity.Entities.Tests/EntityArchetypeQueryTests.cs index b51da57..a049828 100644 --- a/Unity.Entities.Tests/EntityArchetypeQueryTests.cs +++ b/Unity.Entities.Tests/EntityArchetypeQueryTests.cs @@ -134,5 +134,21 @@ public void EntityQueryFilterCannotContainExcludeComponentType_Absent_Throws() queryDesc.Validate(); }); } + + [Test] + [TestRequiresDotsDebugOrCollectionChecks("Test requires entity query safety checks")] + public void EntityQueryFilterCannotContainExcludeComponentType_Present_Throws() + { + var queryDesc = new EntityQueryDesc + { + All = new ComponentType[] {typeof(EcsTestData) }, + Present = new ComponentType[] {typeof(EcsTestData3), ComponentType.Exclude() }, + }; + + Assert.Throws(() => + { + queryDesc.Validate(); + }); + } } } diff --git a/Unity.Entities.Tests/EntityCombinationsTests.cs b/Unity.Entities.Tests/EntityCombinationsTests.cs index c018f81..fbb9357 100644 --- a/Unity.Entities.Tests/EntityCombinationsTests.cs +++ b/Unity.Entities.Tests/EntityCombinationsTests.cs @@ -6,7 +6,6 @@ namespace Unity.Entities.Tests { -#if !UNITY_DOTSRUNTIME partial class EntityCombinationsTests : ECSTestsFixture { public enum ProcessMode @@ -780,5 +779,4 @@ public void JobProcessMixedStress_6([Values] ProcessMode mode, [Values(0, 1, 100 } } -#endif // UNITY_DOTSRUNTIME } diff --git a/Unity.Entities.Tests/EntityCommandBufferDebugViewTests.cs b/Unity.Entities.Tests/EntityCommandBufferDebugViewTests.cs index 3f3a55a..8a41bf3 100644 --- a/Unity.Entities.Tests/EntityCommandBufferDebugViewTests.cs +++ b/Unity.Entities.Tests/EntityCommandBufferDebugViewTests.cs @@ -1,10 +1,10 @@ +using System; using NUnit.Framework; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; namespace Unity.Entities.Tests { -#if !NET_DOTS // NET_DOTS does not support debug views unsafe class EntityCommandBufferDebugViewTests : ECSTestsFixture { [SetUp] @@ -32,9 +32,8 @@ public void EntityCommandBufferDebugView_CreateEntity_ContainsExpectedData() var cmdView = commands[0]; Assert.AreEqual(ECBCommand.CreateEntity, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(32, cmdView.TotalSizeInBytes); -#endif + + Assert.AreEqual(IntPtr.Size == 8 ? 32 : 24, cmdView.TotalSizeInBytes); var createCmdView = cmdView as EntityCommandBuffer.CreateCommandView; Assert.IsFalse(createCmdView.EntityArchetype.Valid); @@ -57,9 +56,7 @@ public void EntityCommandBufferDebugView_CreateEntityFromArchetype_ContainsExpec var cmdView = commands[0]; Assert.AreEqual(ECBCommand.CreateEntity, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(32, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 32 : 24, cmdView.TotalSizeInBytes); var createCmdView = cmdView as EntityCommandBuffer.CreateCommandView; Assert.AreEqual(archetype, createCmdView.EntityArchetype); @@ -82,9 +79,7 @@ public void EntityCommandBufferDebugView_Instantiate_ContainsExpectedData() var cmdView = commands[0]; Assert.AreEqual(ECBCommand.InstantiateEntity, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(32, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(prefab, entityCmdView.Entity); @@ -108,9 +103,7 @@ public void EntityCommandBufferDebugView_InstantiateToArray_ContainsExpectedData var cmdView = commands[0]; Assert.AreEqual(ECBCommand.InstantiateEntity, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(32, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(prefab, entityCmdView.Entity); @@ -133,9 +126,7 @@ public void EntityCommandBufferDebugView_DestroyEntity_ContainsExpectedData() var cmdView = commands[0]; Assert.AreEqual(ECBCommand.DestroyEntity, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(32, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -160,9 +151,7 @@ public void EntityCommandBufferDebugView_AddBuffer_ContainsExpectedData() var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddBuffer, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(112, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 112 : 104, cmdView.TotalSizeInBytes); var bufferCmdView = cmdView as EntityCommandBuffer.EntityBufferCommandView; var typeInfo = TypeManager.GetTypeInfo(); @@ -196,9 +185,7 @@ public void EntityCommandBufferDebugView_AddBufferWithEntityFixup_ContainsExpect var cmdView = commands[1]; Assert.AreEqual(ECBCommand.AddBufferWithEntityFixUp, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(128, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 128 : 120, cmdView.TotalSizeInBytes); var bufferCmdView = cmdView as EntityCommandBuffer.EntityBufferCommandView; var typeInfo = TypeManager.GetTypeInfo(); @@ -232,9 +219,7 @@ public void EntityCommandBufferDebugView_SetBuffer_ContainsExpectedData() var cmdView = commands[0]; Assert.AreEqual(ECBCommand.SetBuffer, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(112, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 112 : 104, cmdView.TotalSizeInBytes); var bufferCmdView = cmdView as EntityCommandBuffer.EntityBufferCommandView; var typeInfo = TypeManager.GetTypeInfo(); @@ -267,9 +252,7 @@ public void EntityCommandBufferDebugView_SetBufferWithEntityFixup_ContainsExpect var cmdView = commands[1]; Assert.AreEqual(ECBCommand.SetBufferWithEntityFixUp, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(128, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 128 : 120, cmdView.TotalSizeInBytes); var bufferCmdView = cmdView as EntityCommandBuffer.EntityBufferCommandView; var typeInfo = TypeManager.GetTypeInfo(); @@ -301,9 +284,7 @@ public void EntityCommandBufferDebugView_AppendToBuffer_ContainsExpectedData() var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AppendToBuffer, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(40, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -334,9 +315,7 @@ public void EntityCommandBufferDebugView_AppendToBufferWithEntityFixup_ContainsE var cmdView = commands[1]; Assert.AreEqual(ECBCommand.AppendToBufferWithEntityFixUp, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(48, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -368,9 +347,7 @@ public void EntityCommandBufferDebugView_AddComponentWithValue_ContainsExpectedD var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddComponent, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(40, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -400,9 +377,7 @@ public void EntityCommandBufferDebugView_AddComponentWithEntityFixup_ContainsExp var cmdView = commands[1]; Assert.AreEqual(ECBCommand.AddComponentWithEntityFixUp, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(48, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -432,9 +407,7 @@ public void EntityCommandBufferDebugView_AddComponent_ContainsExpectedData() var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddComponent, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(40, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -462,9 +435,7 @@ public void EntityCommandBufferDebugView_AddComponentType_ContainsExpectedData() var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddComponent, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(40, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -494,9 +465,7 @@ public void EntityCommandBufferDebugView_AddComponentTypes_ContainsExpectedData( var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddMultipleComponents, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(104, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -537,9 +506,7 @@ public void EntityCommandBufferDebugView_AddComponentForLinkedEntityGroup_Contai var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddComponentLinkedEntityGroup, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(64, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 64 : 56, cmdView.TotalSizeInBytes); var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(rootEntity, entityCmdView.Entity); @@ -586,9 +553,7 @@ public void EntityCommandBufferDebugView_SetComponentForLinkedEntityGroup_Contai var cmdView = commands[0]; Assert.AreEqual(ECBCommand.SetComponentLinkedEntityGroup, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(64, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 64 : 48, cmdView.TotalSizeInBytes); var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(rootEntity, entityCmdView.Entity); @@ -632,9 +597,7 @@ public void EntityCommandBufferDebugView_ReplaceComponentForLinkedEntityGroup_Co var cmdView = commands[0]; Assert.AreEqual(ECBCommand.ReplaceComponentLinkedEntityGroup, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(40, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(rootEntity, entityCmdView.Entity); @@ -664,9 +627,7 @@ public void EntityCommandBufferDebugView_SetComponentWithValue_ContainsExpectedD var cmdView = commands[0]; Assert.AreEqual(ECBCommand.SetComponent, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(40, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -696,9 +657,7 @@ public void EntityCommandBufferDebugView_SetComponentWithEntityFixup_ContainsExp var cmdView = commands[1]; Assert.AreEqual(ECBCommand.SetComponentWithEntityFixUp, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(48, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -729,9 +688,7 @@ public void EntityCommandBufferDebugView_SetEntityEnabled_ContainsExpectedData() var enabledCommand = commands[0]; Assert.AreEqual(ECBCommand.SetEntityEnabled, enabledCommand.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, enabledCommand.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(32, enabledCommand.TotalSizeInBytes); -#endif var enabledEntityCmdView = enabledCommand as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, enabledEntityCmdView.Entity); @@ -745,9 +702,7 @@ public void EntityCommandBufferDebugView_SetEntityEnabled_ContainsExpectedData() var disabledCommand = commands[1]; Assert.AreEqual(ECBCommand.SetEntityEnabled, disabledCommand.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, disabledCommand.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(32, disabledCommand.TotalSizeInBytes); -#endif var disabledEntityCmdView = disabledCommand as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, disabledEntityCmdView.Entity); @@ -775,9 +730,7 @@ public void EntityCommandBufferDebugView_SetName_ContainsExpectedData() var cmdView = commands[0]; Assert.AreEqual(ECBCommand.SetName, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(96, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -804,9 +757,7 @@ public void EntityCommandBufferDebugView_RemoveComponent_ContainsExpectedData() var cmdView = commands[0]; Assert.AreEqual(ECBCommand.RemoveComponent, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(40, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -834,9 +785,7 @@ public void EntityCommandBufferDebugView_RemoveComponentType_ContainsExpectedDat var cmdView = commands[0]; Assert.AreEqual(ECBCommand.RemoveComponent, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(40, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -867,9 +816,7 @@ public void EntityCommandBufferDebugView_RemoveComponentTypes_ContainsExpectedDa var cmdView = commands[0]; Assert.AreEqual(ECBCommand.RemoveMultipleComponents, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(104, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -903,9 +850,7 @@ public void EntityCommandBufferDebugView_AddComponentForEntityQuery_CaptureAtRec var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddComponentForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(56, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 56 : 40, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator.Handle, multiEntityCmdView.Allocator); @@ -945,9 +890,7 @@ public void EntityCommandBufferDebugView_AddComponentTypeForEntityQuery_CaptureA var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddComponentForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(56, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 56 : 40, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -987,9 +930,7 @@ public void EntityCommandBufferDebugView_AddComponentForEntityQueryWithValue_Con var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddComponentForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(64, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 64 : 48, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesComponentCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -1033,9 +974,7 @@ public void EntityCommandBufferDebugView_AddComponentTypesForEntityQuery_Capture var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddMultipleComponentsForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(120, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 120 : 104, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -1079,9 +1018,7 @@ public void EntityCommandBufferDebugView_AddSharedComponentForEntityQuery_Captur var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddUnmanagedSharedComponentValueForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(80, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 80 : 56, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesComponentCommandView_WithUnmanagedSharedValue; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -1127,9 +1064,7 @@ public void EntityCommandBufferDebugView_AddComponentObjectForEntityQuery_Contai var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddComponentObjectForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(72, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 72 : 48, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -1173,9 +1108,7 @@ public void EntityCommandBufferDebugView_SetComponentObjectForEntityQuery_Contai var cmdView = commands[0]; Assert.AreEqual(ECBCommand.SetComponentObjectForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(72, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 72 : 48, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -1219,9 +1152,7 @@ public void EntityCommandBufferDebugView_SetSharedComponentForEntityQuery_Captur var cmdView = commands[0]; Assert.AreEqual(ECBCommand.SetUnmanagedSharedComponentValueForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(80, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 80 : 56, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -1264,9 +1195,7 @@ public void EntityCommandBufferDebugView_RemoveComponentTypeForEntityQuery_Captu var cmdView = commands[0]; Assert.AreEqual(ECBCommand.RemoveComponentForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(56, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 56 : 40, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -1306,9 +1235,7 @@ public void EntityCommandBufferDebugView_RemoveComponentForEntityQuery_CaptureAt var cmdView = commands[0]; Assert.AreEqual(ECBCommand.RemoveComponentForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(56, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 56 : 40, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -1351,9 +1278,7 @@ public void EntityCommandBufferDebugView_RemoveComponentTypesForEntityQuery_Capt var cmdView = commands[0]; Assert.AreEqual(ECBCommand.RemoveMultipleComponentsForMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(120, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 120 : 104, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -1397,9 +1322,7 @@ public void EntityCommandBufferDebugView_DestroyEntitiesForEntityQuery_ContainsE var cmdView = commands[0]; Assert.AreEqual(ECBCommand.DestroyMultipleEntities, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) - Assert.AreEqual(48, cmdView.TotalSizeInBytes); -#endif + Assert.AreEqual(IntPtr.Size == 8 ? 48 : 32, cmdView.TotalSizeInBytes); var multiEntityCmdView = cmdView as EntityCommandBuffer.MultipleEntitiesCommandView; Assert.AreEqual(ecb.m_Data->m_Allocator, multiEntityCmdView.Allocator); @@ -1431,9 +1354,7 @@ public void EntityCommandBufferDebugView_AddSharedComponent_ContainsExpectedData var cmdView = commands[0]; Assert.AreEqual(ECBCommand.AddUnmanagedSharedComponentData, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(48, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -1463,9 +1384,7 @@ public void EntityCommandBufferDebugView_SetSharedComponent_ContainsExpectedData var cmdView = commands[0]; Assert.AreEqual(ECBCommand.SetUnmanagedSharedComponentData, cmdView.CommandType); Assert.AreEqual(ecb.MainThreadSortKey, cmdView.SortKey); -#if UNITY_64 // Comparison assumes 64-bit types were used (remove this check when DOTS-7037 is complete) Assert.AreEqual(48, cmdView.TotalSizeInBytes); -#endif var entityCmdView = cmdView as EntityCommandBuffer.EntityCommandView; Assert.AreEqual(ent, entityCmdView.Entity); @@ -1480,5 +1399,4 @@ public void EntityCommandBufferDebugView_SetSharedComponent_ContainsExpectedData Assert.AreEqual("Set EcsTestSharedComp UnmanagedSharedComponentData",sharedComponentCmdView.ToString()); } } -#endif - } +} diff --git a/Unity.Entities.Tests/EntityCommandBufferTests.cs b/Unity.Entities.Tests/EntityCommandBufferTests.cs index dab21d1..db46ddc 100644 --- a/Unity.Entities.Tests/EntityCommandBufferTests.cs +++ b/Unity.Entities.Tests/EntityCommandBufferTests.cs @@ -273,8 +273,6 @@ public void CanCreateCommandBuffer_FromBurstedISystem_AndPlaybackSucceeds() #if ENABLE_UNITY_COLLECTIONS_CHECKS - // https://unity3d.atlassian.net/browse/DOTSR-1432 - [IgnoreInPortableTests("There are Assert.Throws in the WriteJob, which the runner doesn't find or support.")] [Test] public void EntityCommandBufferSystem_DisposeAfterPlaybackError_Succeeds() { @@ -300,12 +298,7 @@ public void EntityCommandBufferSystem_DisposeAfterPlaybackError_Succeeds() job.Complete(); } - // These tests require: - // - JobsDebugger support for static safety IDs (added in 2020.1) -#if !UNITY_DOTSRUNTIME [Test] - [DotsRuntimeFixme("Static safety IDs - DOTSR-1432")] - [IgnoreInPortableTests("There are Assert.Throws which the runner doesn't find or support.")] public void EntityCommandBufferConcurrent_PlaybackDuringWrite_UsesCustomOwnerTypeName() { EntityCommandBuffer cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); @@ -318,9 +311,6 @@ public void EntityCommandBufferConcurrent_PlaybackDuringWrite_UsesCustomOwnerTyp job.Complete(); } -#endif - - [Test] public void SingleWriterEnforced() { @@ -603,11 +593,7 @@ public void CreateTwoComponents() [Test] public void TestMultiChunks() { -#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; -#endif var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator, PlaybackPolicy.MultiPlayback); cmds.MinimumChunkSize = 512; @@ -1045,7 +1031,6 @@ public void SetSharedComponentDefault() [Test] public void SetSharedComponentNonDefault() { -#if !UNITY_DOTSRUNTIME var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator, PlaybackPolicy.MultiPlayback); var index = TypeManager.GetTypeIndex(); @@ -1070,7 +1055,6 @@ public void SetSharedComponentNonDefault() Assert.AreEqual(2, sharedCompList.Count); Assert.AreEqual(10, sharedCompList[1].value); } -#endif } [Test] @@ -2631,6 +2615,21 @@ public void PlaybackInvalidatesBuffers() }); } + [Test] + public void DisposeBeforePlayback_DynamicBufferAllocationsDontLeak() + { + // Ideally, we'd assert that the following sequence of calls does not leak any memory. + + // Using a rewindable allocator here should normally bypass most of ECB.Dispose(), but the presence of + // DynamicBuffer commands requires the full dispose code to run anyway. + var ecb = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + var e = ecb.CreateEntity(); + var db = ecb.AddBuffer(e); + var elements = CollectionHelper.CreateNativeArray(db.Capacity * 2, Allocator.Temp); + db.AddRange(elements); + ecb.Dispose(); + } + [Test(Description = "Once a buffer command is played back, it has no side effects on the ECB.")] public void BufferChanged_BetweenPlaybacks_HasNoEffectOnECB() { @@ -3569,7 +3568,6 @@ public void BufferCopyFromNativeSliceDoesNotThrowInJob() } [Test] - [ManagedExceptionInPortableTests] // This test relies on side-effects of running exception generating code [TestRequiresDotsDebugOrCollectionChecks("Test requires entity command buffer safety checks")] public void EntityCommandBufferSystemPlaybackExceptionIsolation() { @@ -3815,8 +3813,6 @@ public void InstantiateEntity_BatchMode_DisabledIfEntityDirty() Assert.AreEqual(12, m_Manager.GetComponentData(realDst1).value1); } -#if !UNITY_PORTABLE_TEST_RUNNER - // The portable test runner can't handle the return value from Assert.Throws [Test] [TestRequiresDotsDebugOrCollectionChecks("Test requires entity command buffer safety checks")] public void UninitializedEntityCommandBufferThrows() @@ -3865,7 +3861,6 @@ public void AddOrSetBufferWithEntity_NeedsFixup_ThrowsOnMultiplePlayback([Values Assert.Throws(() => cmds.Playback(m_Manager)); Assert.Throws(() => cmds.Playback(m_Manager2)); } -#endif [Test] public void AddOrSetBufferWithEntity_NeedsFixup_ContainsRealizedEntity([Values(true, false)] bool setBuffer) @@ -4306,25 +4301,17 @@ public void CommandsForLinkedEntityGroup_ComponentWithEntityReference_Throws() { var defferedEntity = cmds.CreateEntity(); -#if !UNITY_PORTABLE_TEST_RUNNER var addEx = Assert.Throws(() => cmds.AddComponentForLinkedEntityGroup(rootEntity, mask, new EcsTestDataEntity{value0 = 42, value1 = defferedEntity})); Assert.IsTrue(addEx.Message.Contains("command contains a reference to a temporary Entity")); var setEx = Assert.Throws(() => cmds.SetComponentForLinkedEntityGroup(rootEntity, mask, new EcsTestDataEntity{value0 = 42, value1 = defferedEntity})); Assert.IsTrue(setEx.Message.Contains("command contains a reference to a temporary Entity")); var replaceEx = Assert.Throws(() => cmds.ReplaceComponentForLinkedEntityGroup(rootEntity, new EcsTestDataEntity{value0 = 42, value1 = defferedEntity})); Assert.IsTrue(replaceEx.Message.Contains("command contains a reference to a temporary Entity")); -#else - Assert.Throws(() => cmds.AddComponentForLinkedEntityGroup(rootEntity, mask, new EcsTestDataEntity{value0 = 42, value1 = defferedEntity})); - Assert.Throws(() => cmds.SetComponentForLinkedEntityGroup(rootEntity, mask, new EcsTestDataEntity{value0 = 42, value1 = defferedEntity})); - Assert.Throws(() => cmds.ReplaceComponentForLinkedEntityGroup(rootEntity, new EcsTestDataEntity{value0 = 42, value1 = defferedEntity})); -#endif } array.Dispose(); } -#if !UNITY_PORTABLE_TEST_RUNNER -// https://unity3d.atlassian.net/browse/DOTSR-1432 void VerifyCommand_Or_CheckThatItThrowsIfEntityIsDeferred(bool shouldThrow, TestDelegate code) { if (shouldThrow) @@ -4450,8 +4437,6 @@ public void DeferredEntities_UsedInTheEntityManager_ShouldThrow() } } -#endif - [Test] public void IsEmpty_Works() { @@ -6940,6 +6925,92 @@ public void AddManagedComponent_DisposeFromBurst_CleanedUp() Assert.DoesNotThrow(() => { DisposeEcb(ref cmds); }); } + [Test] + [TestRequiresDotsDebugOrCollectionChecks("Test requires entity command buffer safety checks")] + public void MoveManagedComponent_Works() + { + var srcEntity = m_Manager.CreateEntity(); + var dstEntity = m_Manager.CreateEntity(); + var destroyedEntity = m_Manager.CreateEntity(); + m_Manager.DestroyEntity(destroyedEntity); + { + // null src is a recording-time error + using var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + Assert.Throws(() => cmds.MoveComponent(Entity.Null, dstEntity)); + } + { + // null dst is a recording-time error + using var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + Assert.Throws(() => cmds.MoveComponent(srcEntity, Entity.Null)); + } + { + // invalid but non-null src is a playback-time error + using var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + cmds.MoveComponent(destroyedEntity, dstEntity); + Assert.Throws(() => cmds.Playback(m_Manager)); + } + { + // invalid but non-null dst is a playback-time error + using var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + cmds.MoveComponent(srcEntity, destroyedEntity); + Assert.Throws(() => cmds.Playback(m_Manager)); + } + { + // missing target component on src is a playback-time error + using var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + cmds.MoveComponent(srcEntity, dstEntity); + Assert.Throws(() => cmds.Playback(m_Manager)); + } + + var value1 = new EcsTestDisposableManagedComponent { Value = "value1" }; + var value2 = new EcsTestDisposableManagedComponent { Value = "value2" }; + m_Manager.AddComponentData(srcEntity, value1); + { + // src == dst is a no-op + using var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + cmds.MoveComponent(srcEntity, srcEntity); + Assert.DoesNotThrow(() => cmds.Playback(m_Manager)); + Assert.AreEqual(value1, m_Manager.GetComponentData(srcEntity)); + Assert.IsFalse(m_Manager.HasComponent(dstEntity)); + } + { + // If dst doesn't have the target component, it's added, and the value is not disposed + using var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + cmds.MoveComponent(srcEntity, dstEntity); + Assert.DoesNotThrow(() => cmds.Playback(m_Manager)); + Assert.IsFalse(m_Manager.HasComponent(srcEntity)); + Assert.AreEqual(value1, m_Manager.GetComponentData(dstEntity)); + } + { + // dst's T is null / default-initialized should work + m_Manager.AddComponentData(srcEntity, value1); + m_Manager.AddComponent(dstEntity); + using var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + cmds.MoveComponent(srcEntity, dstEntity); + Assert.DoesNotThrow(() => cmds.Playback(m_Manager)); + Assert.IsFalse(m_Manager.HasComponent(srcEntity)); + Assert.AreEqual(value1, m_Manager.GetComponentData(dstEntity)); + } + { + // dst's T value matches src's: should work + m_Manager.AddComponentData(srcEntity, value1); + using var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + cmds.MoveComponent(srcEntity, dstEntity); + Assert.DoesNotThrow(() => cmds.Playback(m_Manager)); + Assert.IsFalse(m_Manager.HasComponent(srcEntity)); + Assert.AreEqual(value1, m_Manager.GetComponentData(dstEntity)); + } + { + // dst's T value does not match src. In this case, we need to make sure dst's old value is correctly disposed and doesn't leak. + m_Manager.AddComponentData(srcEntity, value2); + using var cmds = new EntityCommandBuffer(World.UpdateAllocator.ToAllocator); + cmds.MoveComponent(srcEntity, dstEntity); + Assert.DoesNotThrow(() => cmds.Playback(m_Manager)); + Assert.IsFalse(m_Manager.HasComponent(srcEntity)); + Assert.AreEqual(value2, m_Manager.GetComponentData(dstEntity)); + } + } + [Test] [TestRequiresDotsDebugOrCollectionChecks("Test requires entity command buffer safety checks")] public void PlaybackWithTrace_AddManagedComponent() diff --git a/Unity.Entities.Tests/EntityDebugProxyTests.cs b/Unity.Entities.Tests/EntityDebugProxyTests.cs index d163e72..fe5d944 100644 --- a/Unity.Entities.Tests/EntityDebugProxyTests.cs +++ b/Unity.Entities.Tests/EntityDebugProxyTests.cs @@ -3,8 +3,6 @@ using Unity.Collections; using Unity.Jobs; -#if !NET_DOTS - namespace Unity.Entities.Tests { partial class EntityDebugProxyTests : ECSTestsFixture @@ -253,5 +251,3 @@ public void DoesntIncludeMetaChunkEntities() } } } - -#endif diff --git a/Unity.Entities.Tests/EntityManagerBugTests.cs b/Unity.Entities.Tests/EntityManagerBugTests.cs index 63ae6a2..7ce087a 100644 --- a/Unity.Entities.Tests/EntityManagerBugTests.cs +++ b/Unity.Entities.Tests/EntityManagerBugTests.cs @@ -130,53 +130,6 @@ private void SanityCheckVersions() } } -#if UNITY_64 // Tests rely on validating a bug in 64-bit builds specifically - class Bug1294 : ECSTestsFixture - { - // Test only applies to 64-bit builds - [Test] - public unsafe void EntityInChunkCompareTo_Low32BitsMatch_ComparesCorrectly() - { - // Create a valid Entity and get its EntityInChunk: - var ent1 = m_Manager.CreateEntity(typeof(EcsTestData)); - var eic1 = m_Manager.GetCheckedEntityDataAccess()->EntityComponentStore->GetEntityInChunk(ent1); - // Construct an artificial EntityInChunk for a hypothetical entity in a hypothetical - // Chunk, exactly 2^32 bytes higher than ent1's (such that comparing only 32 bits of - // the chunk pointer would fail): - var eic2 = new EntityInChunk - { - Chunk = (Chunk*)((ulong)eic1.Chunk + (1ul << 32)), - IndexInChunk = eic1.IndexInChunk, - }; - Assert.Greater((ulong)eic2.Chunk, (ulong)eic1.Chunk); - Assert.AreEqual((int)eic2.Chunk, (int)eic1.Chunk); - // Make sure the EntityInChunks sort correctly (eic2 > eic1): - Assert.Greater(eic2.CompareTo(eic1), 0); - Assert.Less(eic1.CompareTo(eic2), 0); - } - - [Test] - public unsafe void EntityInChunkCompareTo_DifferenceOverflows32Bits_ComparesCorrectly() - { - // Create a valid Entity and get its EntityInChunk: - var ent1 = m_Manager.CreateEntity(typeof(EcsTestData)); - var eic1 = m_Manager.GetCheckedEntityDataAccess()->EntityComponentStore->GetEntityInChunk(ent1); - // Construct an artificial EntityInChunk for a hypothetical entity in a hypothetical - // Chunk, whose pointer is just above 2^31 larger than ent1's (such that using lhs-rhs as the - // CompareTo result would overflow a signed int and give an incorrect result): - var eic2 = new EntityInChunk - { - Chunk = (Chunk*)((ulong)eic1.Chunk + (1ul << 31) + 65536), - IndexInChunk = eic1.IndexInChunk, - }; - Assert.Greater((ulong)eic2.Chunk, (ulong)eic1.Chunk); - Assert.Less((int)((ulong)eic2.Chunk - (ulong)eic1.Chunk), 0); - // Make sure the EntityInChunks sort correctly (eic2 > eic1): - Assert.Greater(eic2.CompareTo(eic1), 0); - Assert.Less(eic1.CompareTo(eic2), 0); - } - } -#endif class Bug476 : ECSTestsFixture { [Test] diff --git a/Unity.Entities.Tests/EntityManagerComponentQueryOperationsTests.cs b/Unity.Entities.Tests/EntityManagerComponentQueryOperationsTests.cs index 4988665..7bd07d9 100644 --- a/Unity.Entities.Tests/EntityManagerComponentQueryOperationsTests.cs +++ b/Unity.Entities.Tests/EntityManagerComponentQueryOperationsTests.cs @@ -1439,13 +1439,8 @@ public void AddMultipleComponents_WithQuery_ExceedMaxSharedComponentsThrows() 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.StartsWith($"Cannot add more than {EntityComponentStore.kMaxSharedComponentCount} SharedComponent's to a single Archetype")); -#endif m_ManagerDebug.CheckInternalConsistency(); @@ -1498,7 +1493,7 @@ public void AddMultipleComponents_WithQuery_ExceedChunkCapacityThrows() { var componentTypes = new ComponentTypeSet(typeof(EcsTestDataHuge)); // add really big component(s) - Assert.AreEqual(16320, Chunk.GetChunkBufferSize()); // if chunk size changes, need to update this test + Assert.AreEqual(16320, Chunk.kChunkBufferSize); // 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)); @@ -1509,12 +1504,8 @@ public void AddMultipleComponents_WithQuery_ExceedChunkCapacityThrows() 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(); @@ -1596,7 +1587,6 @@ public void RemoveMultipleComponents_WithQuery_SharedComponentValuesPreserved() } [Test] - [IgnoreInPortableTests("intermittent crash (likely race condition)")] public void RemoveAnyComponent_WithQuery_IgnoresChunksThatDontHaveTheComponent() { var componentTypes = new ComponentType[] @@ -1835,7 +1825,6 @@ public void AddRemoveAnyComponent_WithQuery_WorksWithVariousTypes_ManagedCompone } [Test] - [IgnoreInPortableTests("intermittent crash (likely race condition)")] public void RemoveAnyComponent_WithGroup_IgnoresChunksThatDontHaveTheComponent_ManagedComponents() { var componentTypes = new ComponentType[] diff --git a/Unity.Entities.Tests/EntityManagerPrefabTests.cs b/Unity.Entities.Tests/EntityManagerPrefabTests.cs index 2f229e7..9a4f908 100644 --- a/Unity.Entities.Tests/EntityManagerPrefabTests.cs +++ b/Unity.Entities.Tests/EntityManagerPrefabTests.cs @@ -126,8 +126,7 @@ public void DestroyEmptyLinkedEntityGroup() } [Test] - [IgnoreInPortableTests("Throws a NotImplemented exception")] - public void InstantiateExplicitEntitySet([Values] bool instantiate) + public void CopyExplicitEntitySet() { var external = m_Manager.CreateEntity(); var a = m_Manager.CreateEntity(); @@ -138,16 +137,8 @@ public void InstantiateExplicitEntitySet([Values] bool instantiate) using (var inputs = CollectionHelper.CreateNativeArray(new[] { a, b }, ref World.UpdateAllocator)) using (var outputs = CollectionHelper.CreateNativeArray(2, ref World.UpdateAllocator)) { - if (instantiate) - { - m_Manager.Instantiate(inputs, outputs); - Assert.IsFalse(m_Manager.HasComponent(outputs[1])); - } - else - { - m_Manager.CopyEntities(inputs, outputs); - Assert.IsTrue(m_Manager.HasComponent(outputs[1])); - } + m_Manager.CopyEntities(inputs, outputs); + Assert.IsTrue(m_Manager.HasComponent(outputs[1])); Assert.AreEqual(outputs[1], m_Manager.GetComponentData(outputs[0]).value1); Assert.AreEqual(external, m_Manager.GetComponentData(outputs[1]).value1); @@ -217,7 +208,6 @@ public void CheckLinkedGroup_ManagedComponents(Entity clone, NativeArray } [Test] - [DotsRuntimeFixme] // No Unity.Properties support public void InstantiateLinkedGroup_ManagedComponents() { var external = m_Manager.CreateEntity(); @@ -236,7 +226,6 @@ public void InstantiateLinkedGroup_ManagedComponents() } [Test] - [DotsRuntimeFixme] // No Unity.Properties support public void InstantiateLinkedGroupStressTest_ManagedComponents([Values(1, 1023)] int count) { var external = m_Manager.CreateEntity(); diff --git a/Unity.Entities.Tests/EntityManagerSafetyTests.cs b/Unity.Entities.Tests/EntityManagerSafetyTests.cs index 6427fce..1ebccfe 100644 --- a/Unity.Entities.Tests/EntityManagerSafetyTests.cs +++ b/Unity.Entities.Tests/EntityManagerSafetyTests.cs @@ -1,7 +1,5 @@ using NUnit.Framework; using System; -#if !NET_DOTS -#endif namespace Unity.Entities.Tests { @@ -60,10 +58,6 @@ public void AddComponentTwiceIgnored() Assert.AreEqual(testData.value, 2); } -#if !NET_DOTS -// https://unity3d.atlassian.net/browse/DOTSR-1432 -// EntitiesAssert isn't currently supported - [Test] public void RemoveComponentTwiceIgnored() { @@ -82,8 +76,6 @@ public void RemoveComponentTwiceIgnored() Assert.That(removed1, Is.False); } -#endif - [Test] public void RemoveSharedComponentTwiceIgnored() { diff --git a/Unity.Entities.Tests/EntityManagerTests.cs b/Unity.Entities.Tests/EntityManagerTests.cs index 8aa251e..6c35533 100644 --- a/Unity.Entities.Tests/EntityManagerTests.cs +++ b/Unity.Entities.Tests/EntityManagerTests.cs @@ -9,6 +9,7 @@ using Unity.Core; using Unity.Jobs; using UnityEngine; +using Unity.Jobs.LowLevel.Unsafe; namespace Unity.Entities.Tests { @@ -160,14 +161,14 @@ public void AddComponentEmptyNativeArray() array.Dispose(); } - unsafe public bool IndexInChunkIsValid(Entity entity) + public unsafe bool IndexInChunkIsValid(Entity entity) { var entityInChunk = m_Manager.GetCheckedEntityDataAccess()->EntityComponentStore->GetEntityInChunk(entity); - return entityInChunk.IndexInChunk < entityInChunk.Chunk->Count; + return entityInChunk.IndexInChunk < entityInChunk.Chunk.Count; } [Test] - unsafe public void AddComponentNativeArrayCorrectChunkIndexAfterPacking() + public void AddComponentNativeArrayCorrectChunkIndexAfterPacking() { // This test checks for the bug revealed here https://github.com/Unity-Technologies/dots/issues/2133 // When packing was done, it was possible for the packed entities to have an incorrect @@ -189,10 +190,6 @@ unsafe public void AddComponentNativeArrayCorrectChunkIndexAfterPacking() Assert.IsTrue(IndexInChunkIsValid(checkEntity2)); } -#if !NET_DOTS - // https://unity3d.atlassian.net/browse/DOTSR-1432 - // In IL2CPP this segfaults at runtime. There is a crash in GetAssignableComponentTypes() - [Test] public void FoundComponentInterface() { @@ -217,8 +214,6 @@ public void FoundComponentInterfaceNonAllocating() Assert.AreEqual(0, barTypes.Count); } -#endif - [Test] public void VersionIsConsistent() { @@ -268,7 +263,6 @@ struct TestInterfaceComponent : TestInterface, IComponentData } [Test] - [DotsRuntimeFixme] public void GetComponentBoxedSupportsInterface() { var entity = m_Manager.CreateEntity(); @@ -281,7 +275,6 @@ public void GetComponentBoxedSupportsInterface() } [Test] - [DotsRuntimeFixme] [TestRequiresDotsDebugOrCollectionChecks("Test requires entity data access safety checks")] public void GetComponentBoxedThrowsWhenInterfaceNotFound() { @@ -399,10 +392,6 @@ struct MaxCapacityTag2 : IComponentData {} [MaximumChunkCapacity(3)] struct MaxCapacityTag3 : IComponentData {} -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1432 - // TODO: IL2CPP_TEST_RUNNER doesn't support TestCase(typeof) - [TestCase(typeof(MaxCapacityTag1))] [TestCase(typeof(MaxCapacityTag2))] [TestCase(typeof(MaxCapacityTag3))] @@ -424,8 +413,6 @@ public unsafe void ChunkComponentRunIsAligned(Type maxCapacityTagType) Assert.True(CollectionHelper.IsAligned(p2, CollectionHelper.CacheLineSize)); } -#endif - struct WillFitWithAlign : IComponentData { // sizeof(T) is not a constant @@ -787,8 +774,11 @@ public unsafe void ValidatePaddingBytesAreOverwrittenWhenReusingChunks() } Assert.AreEqual(2, dppArchetype.ChunkCount); - for (int i = 0; i < dppArchetype.Archetype->Chunks.Count; ++i) - seenChunkBuffers.Add((ulong)dppArchetype.Archetype->Chunks[i]->Buffer); + { + var chunks = dppArchetype.Archetype->Chunks; + for (int i = 0, count = chunks.Count; i < count; ++i) + seenChunkBuffers.Add((ulong)chunks[i].Buffer); + } // Validate the components have the byte pattern we expect foreach (var entity in entities) @@ -823,7 +813,7 @@ public unsafe void ValidatePaddingBytesAreOverwrittenWhenReusingChunks() m_Manager.SetComponentData(entity, component); } Assert.AreEqual(1, pdpArchetype.ChunkCount); - Assert.IsTrue(seenChunkBuffers.Contains((ulong)pdpArchetype.Archetype->Chunks[0]->Buffer)); + Assert.IsTrue(seenChunkBuffers.Contains((ulong)pdpArchetype.Archetype->Chunks[0].Buffer)); // Validate the components have zero initialized padding bytes and not the poisoned padding // i.e. what you store to chunk memory is what you get. You are not affected by the @@ -856,11 +846,7 @@ public void Fix1602() endGroup.Dispose(); } - // These tests require: - // - JobsDebugger support for static safety IDs (added in 2020.1) - // - Asserting throws -#if !UNITY_DOTSRUNTIME - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Requires Atomic Safety Handles for dispose checks")] public void EntityManager_DoubleDispose_UsesCustomOwnerTypeName() { @@ -872,8 +858,6 @@ public void EntityManager_DoubleDispose_UsesCustomOwnerTypeName() .With.Message.Contains("EntityManager")); } -#endif - #if !UNITY_DISABLE_MANAGED_COMPONENTS class TestInterfaceManagedComponent : TestInterface, IComponentData { @@ -908,7 +892,6 @@ public void VersionIsConsistent_ManagedComponents() } [Test] - [DotsRuntimeFixme] // Would need UnsafeUtility.PinSystemObjectAndGetAddress public void GetComponentBoxedSupportsInterface_ManagedComponent() { var entity = m_Manager.CreateEntity(); @@ -1082,10 +1065,7 @@ public ComplexManagedComponent() public MyClass Class; } - // https://unity3d.atlassian.net/browse/DOTSR-1432 - // TODO the il2cpp test runner doesn't Assert.AreSame/AreNotSame [Test] - [DotsRuntimeFixme] // Unity.Properties support public void Instantiate_DeepClone_ManagedComponents() { var entity = m_Manager.CreateEntity(); @@ -1138,12 +1118,7 @@ class ManagedComponentWithArray : IComponentData public int[] IntArray; } -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1432 - // TODO: IL2CPP_TEST_RUNNER doesn't broadly support the That / Constraint Model. Note this test case is also flagged DotsRuntimeFixme. - [Test] - [DotsRuntimeFixme] // Requires Unity.Properties support public void Instantiate_DeepClone_ManagedComponentWithZeroSizedArray() { var originalEntity = m_Manager.CreateEntity(); @@ -1172,8 +1147,6 @@ public void Instantiate_DeepClone_ManagedComponentWithNullArray() Assert.That(instanceComponent.IntArray, Is.Null); } -#endif // UNITY_PORTABLE_TEST_RUNNER - class ManagedComponentWithNestedClass : IComponentData { #pragma warning disable 649 @@ -1186,9 +1159,6 @@ public class NestedClass } #pragma warning restore 649 -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1432 - // TODO: IL2CPP_TEST_RUNNER doesn't broadly support the That / Constraint Model. Note this is also flagged DotsRuntimeFixme. [Test] public void Instantiate_DeepClone_ManagedComponentWithNullReferenceType() { @@ -1878,8 +1848,6 @@ public void EntityName_Disable_GetName_SetName_Fixed() Assert.AreEqual(new FixedString64Bytes(), newName); } #endif - -#endif // UNITY_PORTABLE_TEST_RUNNER #endif } } diff --git a/Unity.Entities.Tests/EntityQueryDelta.cs b/Unity.Entities.Tests/EntityQueryDelta.cs index 9849360..5acd79d 100644 --- a/Unity.Entities.Tests/EntityQueryDelta.cs +++ b/Unity.Entities.Tests/EntityQueryDelta.cs @@ -192,7 +192,6 @@ public void NoChangeEntityWrap() deltaCheckSystem.UpdateExpectedResults(nothing); } -#if !UNITY_DOTSRUNTIME public partial class DeltaProcessComponentSystem : SystemBase { protected override void OnUpdate() @@ -663,7 +662,6 @@ public void ChangeFilterWorksWithTwoTypes() Assert.AreEqual(100, m_Manager.GetComponentData(e).value); } -#endif partial class SpawnerSystem : SystemBase { diff --git a/Unity.Entities.Tests/EntityQuerySyncChangeFilterTypesTests.cs b/Unity.Entities.Tests/EntityQuerySyncChangeFilterTypesTests.cs index f4c0782..0418f71 100644 --- a/Unity.Entities.Tests/EntityQuerySyncChangeFilterTypesTests.cs +++ b/Unity.Entities.Tests/EntityQuerySyncChangeFilterTypesTests.cs @@ -1,7 +1,3 @@ -#if !UNITY_DOTSRUNTIME -// TODO: IL2CPP_TEST_RUNNER doesn't support TextFixture with argument and other calls. Note these -// are also generally flagged with DotsRuntimeFixme. - using System; using NUnit.Framework; using Unity.Collections; @@ -176,4 +172,3 @@ public void EntityManager_AddSharedComponentDataWithEntityQuery_Syncs_ChangeFilt } } } -#endif // !UNITY_DOTSRUNTIME diff --git a/Unity.Entities.Tests/EntityQueryTests.cs b/Unity.Entities.Tests/EntityQueryTests.cs index def6648..1acc9f1 100644 --- a/Unity.Entities.Tests/EntityQueryTests.cs +++ b/Unity.Entities.Tests/EntityQueryTests.cs @@ -1,6 +1,3 @@ -#if !NET_DOTS -// https://unity3d.atlassian.net/browse/DOTSR-1432 - using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -107,6 +104,29 @@ public void ResetFilter_Works() Assert.AreEqual(1, query.CalculateChunkCount(), "ResetFilter did not clear order filter"); } + [Test] + [TestRequiresDotsDebugOrCollectionChecks()] + public void SetSharedComponentFilter_ComponentNotRequired_Throws() + { + var filterValue = new EcsTestSharedComp(17); + // Setting a shared component filter for a component that is required in the query description should work. + using var queryAll = new EntityQueryBuilder(Allocator.Temp).WithAll().Build(m_Manager); + Assert.DoesNotThrow(() => queryAll.SetSharedComponentFilter(filterValue)); + // WithDisabled() already throws if T is not enableable, and shared components can't currently be enableable, so this + // use case is impossible. + //using var queryDisabled = new EntityQueryBuilder(Allocator.Temp).WithDisabled().Build(m_Manager); + //Assert.DoesNotThrow(() => queryDisabled.SetSharedComponentFilter(filterValue)); + using var queryPresent = new EntityQueryBuilder(Allocator.Temp).WithPresent().Build(m_Manager); + Assert.DoesNotThrow(() => queryPresent.SetSharedComponentFilter(filterValue)); + // For components that are not required, setting a shared component filter should throw. + using var queryAny = new EntityQueryBuilder(Allocator.Temp).WithAny().Build(m_Manager); + Assert.Throws(() => queryAny.SetSharedComponentFilter(filterValue)); + using var queryNone = new EntityQueryBuilder(Allocator.Temp).WithNone().Build(m_Manager); + Assert.Throws(() => queryNone.SetSharedComponentFilter(filterValue)); + using var queryAbsent = new EntityQueryBuilder(Allocator.Temp).WithAbsent().Build(m_Manager); + Assert.Throws(() => queryAbsent.SetSharedComponentFilter(filterValue)); + } + [Test] public void ToArchetypeChunkArray_Works() { @@ -1582,7 +1602,7 @@ unsafe public void CalculateFilteredChunkIndexArray_Works([Values] bool enableCh var queryFilter = query._GetImpl()->_Filter; for (int unfilteredChunkIndex = 0; unfilteredChunkIndex < unfilteredChunks.Length; ++unfilteredChunkIndex) { - var chunk = unfilteredChunks.Ptr[unfilteredChunkIndex]; + var chunk = unfilteredChunks.ChunkIndices[unfilteredChunkIndex]; int chunkIndexInArchetype = unfilteredChunks.ChunkIndexInArchetype->Ptr[unfilteredChunkIndex]; int matchingArchetypeIndex = unfilteredChunks.PerChunkMatchingArchetypeIndex->Ptr[unfilteredChunkIndex]; var matchingArchetype = matchingArchetypes.Ptr[matchingArchetypeIndex]; @@ -1663,7 +1683,7 @@ unsafe public void CalculateFilteredChunkIndexArrayAsync_Works([Values] bool ena var queryFilter = query._GetImpl()->_Filter; for (int unfilteredChunkIndex = 0; unfilteredChunkIndex < unfilteredChunks.Length; ++unfilteredChunkIndex) { - var chunk = unfilteredChunks.Ptr[unfilteredChunkIndex]; + var chunk = unfilteredChunks.ChunkIndices[unfilteredChunkIndex]; int chunkIndexInArchetype = unfilteredChunks.ChunkIndexInArchetype->Ptr[unfilteredChunkIndex]; int matchingArchetypeIndex = unfilteredChunks.PerChunkMatchingArchetypeIndex->Ptr[unfilteredChunkIndex]; var matchingArchetype = matchingArchetypes.Ptr[matchingArchetypeIndex]; @@ -1958,10 +1978,6 @@ private void MakeExtraQueries(int size) } } -#if !UNITY_PORTABLE_TEST_RUNNER - // https://unity3d.atlassian.net/browse/DOTSR-1432 - // TODO: IL2CPP_TEST_RUNNER can't handle Assert.That combined with Throws - [Test] [TestRequiresDotsDebugOrCollectionChecks("Test requires entity query safety checks")] public void GetEntityQueryMaskThrowsOnOverflow() @@ -1970,8 +1986,6 @@ public void GetEntityQueryMaskThrowsOnOverflow() Throws.Exception.With.Message.Matches("You have reached the limit of 1024 unique EntityQueryMasks, and cannot generate any more.")); } -#endif - [Test] public unsafe void GetEntityQueryMask_ReturnsCachedMask() { @@ -3113,6 +3127,22 @@ public void EntityQueryBuilder_WithAbsent_Works() query.ToEntityArray(Allocator.Temp).ToArray()); } + [Test] + public void EntityQueryBuilder_WithPresent_Works() + { + var entity1 = m_Manager.CreateEntity(typeof(EcsTestDataEnableable)); + var entity2 = m_Manager.CreateEntity(typeof(EcsTestDataEnableable2)); + var entity12 = m_Manager.CreateEntity(typeof(EcsTestDataEnableable), typeof(EcsTestDataEnableable2)); + m_Manager.SetComponentEnabled(entity12,false); + + var query = new EntityQueryBuilder(Allocator.Temp) + .WithPresent() + .Build(EmptySystem); + + CollectionAssert.AreEquivalent(new[]{entity2,entity12}, + query.ToEntityArray(Allocator.Temp).ToArray()); + } + [Test] public void EntityQueryBuilder_ConstructedWithoutAllocator_Throws() { @@ -3324,7 +3354,6 @@ public unsafe void MultipleQueryISystemWithSourcegen_EntityQueryCache_ValidAfter Assert.AreEqual(4,systemState->EntityQueries.Capacity); } -#if !UNITY_DOTSRUNTIME public partial class CachedSystemQueryTestSystem : SystemBase { protected override void OnCreate() @@ -3363,8 +3392,6 @@ public void CachedSystemQueryReturnsOnlyExactQuery() Assert.AreEqual(queryA, queryB); } -#endif // !UNITY_DOTSRUNTIME - #if !UNITY_DISABLE_MANAGED_COMPONENTS private class ManagedComponent : IComponentData { @@ -3985,6 +4012,7 @@ public unsafe void EntityQueryBuilder_CreateBuilder() var builder = new EntityQueryBuilder(Allocator.Temp); builder.WithAll(); builder.WithNone(); + builder.WithPresent(); var query = builder.Build(EmptySystem); var queryData = query._GetImpl()->_QueryData; @@ -3997,9 +4025,11 @@ public unsafe void EntityQueryBuilder_CreateBuilder() Assert.AreEqual(0, archetypeQuery.AnyCount); Assert.AreEqual(0, archetypeQuery.DisabledCount); Assert.AreEqual(0, archetypeQuery.AbsentCount); + Assert.AreEqual(1, archetypeQuery.PresentCount); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.All[0]); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.None[0]); + Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.Present[0]); builder.Dispose(); } @@ -4010,6 +4040,7 @@ public unsafe void EntityQueryBuilder_CreateBuilder_FluentSyntax() var query = new EntityQueryBuilder(Allocator.Temp) .WithAll() .WithNone() + .WithPresent() .Build(EmptySystem); var queryData = query._GetImpl()->_QueryData; @@ -4022,9 +4053,11 @@ public unsafe void EntityQueryBuilder_CreateBuilder_FluentSyntax() Assert.AreEqual(0, archetypeQuery.AnyCount); Assert.AreEqual(0, archetypeQuery.DisabledCount); Assert.AreEqual(0, archetypeQuery.AbsentCount); + Assert.AreEqual(1, archetypeQuery.PresentCount); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.All[0]); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.None[0]); + Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.Present[0]); } [Test] @@ -4034,6 +4067,7 @@ public unsafe void EntityQueryBuilder_CreateMultipleArchetypeQueries() .WithAll().WithNone() .AddAdditionalQuery() .WithAll().WithAny() + .WithPresent() .Build(EmptySystem); var queryData = query._GetImpl()->_QueryData; @@ -4046,6 +4080,7 @@ public unsafe void EntityQueryBuilder_CreateMultipleArchetypeQueries() Assert.AreEqual(0, archetypeQuery1.AnyCount); Assert.AreEqual(0, archetypeQuery1.DisabledCount); Assert.AreEqual(0, archetypeQuery1.AbsentCount); + Assert.AreEqual(0, archetypeQuery1.PresentCount); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery1.All[0]); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery1.None[0]); @@ -4057,12 +4092,14 @@ public unsafe void EntityQueryBuilder_CreateMultipleArchetypeQueries() Assert.AreEqual(2, archetypeQuery2.AnyCount); Assert.AreEqual(0, archetypeQuery2.DisabledCount); Assert.AreEqual(0, archetypeQuery2.AbsentCount); + Assert.AreEqual(1, archetypeQuery2.PresentCount); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery2.All[0]); Assert.That(ToManagedArray(archetypeQuery2.Any, archetypeQuery2.AnyCount), Is.EquivalentTo(new TypeIndex[] { ComponentType.ReadOnly().TypeIndex, ComponentType.ReadOnly().TypeIndex, })); + Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery2.Present[0]); } [Test] @@ -4087,6 +4124,7 @@ public unsafe void EntityQueryBuilder_CreateMultipleDistinctQueries() Assert.AreEqual(0, archetypeQuery1.AnyCount); Assert.AreEqual(0, archetypeQuery1.DisabledCount); Assert.AreEqual(0, archetypeQuery1.AbsentCount); + Assert.AreEqual(0, archetypeQuery1.PresentCount); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery1.All[0]); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery1.None[0]); @@ -4102,6 +4140,7 @@ public unsafe void EntityQueryBuilder_CreateMultipleDistinctQueries() Assert.AreEqual(2, archetypeQuery2.AnyCount); Assert.AreEqual(0, archetypeQuery2.DisabledCount); Assert.AreEqual(0, archetypeQuery2.AbsentCount); + Assert.AreEqual(0, archetypeQuery2.PresentCount); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery2.All[0]); Assert.That(ToManagedArray(archetypeQuery2.Any, archetypeQuery2.AnyCount), Is.EquivalentTo(new TypeIndex[] { @@ -4245,13 +4284,8 @@ public unsafe void EntityQuery_CreatedByEntityManager_DestroyedByEntityManager() // access violation or other crash. try { -#if UNITY_DOTSRUNTIME - Assert.AreEqual((IntPtr)0xECECECECECECECEC, (IntPtr)queryFromEntityManager.__impl->_QueryData); - Assert.AreEqual((IntPtr)0xECECECECECECECEC, (IntPtr)queryFromBuild.__impl->_QueryData); -#else Assert.AreEqual((IntPtr)0, (IntPtr)queryFromEntityManager.__impl->_QueryData); Assert.AreEqual((IntPtr)0, (IntPtr)queryFromBuild.__impl->_QueryData); -#endif } catch (AccessViolationException e) { @@ -4263,12 +4297,6 @@ public unsafe void EntityQuery_CreatedByEntityManager_DestroyedByEntityManager() } } - // This system was never calling OnUpdate because the query was empty. - // If it does call OnUpdate it fails because burst is disabled for the test config: - // Dots Runtime NS2.0 Smoke Tests macos [trunk DOTS Monorepo] - // TODO FIXME: Once burst is enabled for this test, remove the #if !UNITY_DOTSRUNTIME || UNITY_WINDOWS - // See DotsRuntimeBurstSettings in Unity.Dots.TestRunner.DotsTestRunner.GenerateBuildConfiguration -#if !UNITY_DOTSRUNTIME || UNITY_WINDOWS [BurstCompile(CompileSynchronously = true)] public partial struct BurstCompiledUnmanagedSystemEntityQueryBuilder : ISystem { @@ -4327,7 +4355,6 @@ public void BurstCompiledUnmanagedSystemEntityQueryBuilderWorks() Assert.DoesNotThrow(() => group.Update()); group.CompleteDependencyInternal(); } -#endif //!UNITY_DOTSRUNTIME || UNITY_WINDOWS [Test] public void GetEntityQueryDesc() @@ -4339,6 +4366,7 @@ public void GetEntityQueryDesc() None = new[] { ComponentType.ReadOnly(), ComponentType.ReadWrite() }, Disabled = new[] { ComponentType.ReadOnly(), ComponentType.ReadWrite() }, Absent = new[] { ComponentType.ReadOnly(), ComponentType.ReadWrite() }, + Present = new[] { ComponentType.ReadOnly(), ComponentType.ReadWrite() }, Options = EntityQueryOptions.IncludePrefab | EntityQueryOptions.IncludeDisabledEntities }; using (var query = m_Manager.CreateEntityQuery(queryDesc)) @@ -4798,4 +4826,3 @@ public void EntityQueryInJob_WithExclusiveEntityTransaction_Works() } } } -#endif // NET_DOTS diff --git a/Unity.Entities.Tests/EntityRemapUtilityTests.cs b/Unity.Entities.Tests/EntityRemapUtilityTests.cs index 3805f25..fecb126 100644 --- a/Unity.Entities.Tests/EntityRemapUtilityTests.cs +++ b/Unity.Entities.Tests/EntityRemapUtilityTests.cs @@ -121,7 +121,7 @@ public void CalculateEntityOffsetsReturnsOffsetsOfEmbeddedEntities() Assert.AreEqual(16, offsets[1].Offset); } -#if !UNITY_DOTSRUNTIME && !UNITY_DISABLE_MANAGED_COMPONENTS +#if !UNITY_DISABLE_MANAGED_COMPONENTS // Test uses class component types [Test] public void HasEntityReferencesManaged_Basic() @@ -225,7 +225,7 @@ public void HasEntityReferencesManaged_Recursion() } -#endif // !UNITY_DOTSRUNTIME && !UNITY_DISABLE_MANAGED_COMPONENTS +#endif // !UNITY_DISABLE_MANAGED_COMPONENTS } } diff --git a/Unity.Entities.Tests/EntityTransactionTests.cs b/Unity.Entities.Tests/EntityTransactionTests.cs index 4e3324f..9555957 100644 --- a/Unity.Entities.Tests/EntityTransactionTests.cs +++ b/Unity.Entities.Tests/EntityTransactionTests.cs @@ -1174,5 +1174,56 @@ public unsafe void AllocateConsecutiveEntitiesForLoadingInJob_Works() } + partial class ScheduleJobDuringExclusiveEntityTransactionTestSystem : SystemBase + { + protected override void OnUpdate() + { + try + { + var job = new EmptyJob(); + EntityQuery query = GetEntityQuery(typeof(EcsTestData)); + EntityManager.BeginExclusiveEntityTransaction(); + job.Schedule(query, new JobHandle()).Complete(); + } + finally + { + EntityManager.EndExclusiveEntityTransaction(); + } + } + } + + [Test] + public void ScheduleJobDuringExclusiveEntityTransactionTest_Throws() + { + var system = World.GetOrCreateSystemManaged(); + var exception = Assert.Throws(() => system.Update()); + Assert.AreEqual(exception.Message, "You can't schedule a job while an exclusive transaction is active"); + } + + partial class RunByRefDuringExclusiveEntityTransactionTestSystem : SystemBase + { + protected override void OnUpdate() + { + try + { + var job = new EmptyJob(); + EntityQuery query = GetEntityQuery(typeof(EcsTestData)); + EntityManager.BeginExclusiveEntityTransaction(); + Internal.InternalCompilerInterface.JobChunkInterface.RunByRefWithoutJobs(ref job, query); + } + finally + { + EntityManager.EndExclusiveEntityTransaction(); + } + } + } + + [Test] + public void RunByRefDuringExclusiveEntityTransactionTest_Throws() + { + var system = World.GetOrCreateSystemManaged(); + var exception = Assert.Throws(() => system.Update()); + Assert.AreEqual(exception.Message, "You can't schedule a job while an exclusive transaction is active"); + } } } diff --git a/Unity.Entities.Tests/FastEqualityTests.cs b/Unity.Entities.Tests/FastEqualityTests.cs index 1a96043..7033f3e 100644 --- a/Unity.Entities.Tests/FastEqualityTests.cs +++ b/Unity.Entities.Tests/FastEqualityTests.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME // No UnsafeUtility.GetFieldOffset, so FastEquality doesn't implement CreateTypeInfo() even with Tiny BCL using System.Linq; using System.Runtime.InteropServices; using NUnit.Framework; @@ -474,4 +473,3 @@ public unsafe void EqualsGenericComponentPadding() #endif } } -#endif diff --git a/Unity.Entities.Tests/ForEachCodegen/EntityCommandBuffersInForEachTests.cs b/Unity.Entities.Tests/ForEachCodegen/EntityCommandBuffersInForEachTests.cs index c6f33fb..fae2bc6 100644 --- a/Unity.Entities.Tests/ForEachCodegen/EntityCommandBuffersInForEachTests.cs +++ b/Unity.Entities.Tests/ForEachCodegen/EntityCommandBuffersInForEachTests.cs @@ -39,14 +39,14 @@ public void AddComponentToEntity_WithImmediatePlayback() var entity = EntityManager.CreateEntity(); Entities -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS .WithoutBurst() #endif .WithImmediatePlayback() .ForEach( (Entity e, EntityCommandBuffer ecb) => { -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS ecb.AddComponent(e, new EcsTestManagedComponent()); ecb.AddComponent(e); #endif @@ -57,7 +57,7 @@ public void AddComponentToEntity_WithImmediatePlayback() }) .Run(); -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS Assert.IsTrue(EntityManager.HasComponent(entity)); Assert.IsTrue(EntityManager.HasComponent(entity)); #endif @@ -85,14 +85,14 @@ public void AddComponentToEntity_WithDeferredPlayback(ScheduleType scheduleType) case ScheduleType.Run: { Entities -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS .WithoutBurst() #endif .WithDeferredPlaybackSystem() .ForEach( (Entity e, EntityCommandBuffer ecb) => { -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS ecb.AddComponent(e, new EcsTestManagedComponent()); ecb.AddComponent(e); #endif @@ -150,7 +150,7 @@ public void AddComponentToEntity_WithDeferredPlayback(ScheduleType scheduleType) Assert.IsTrue(EntityManager.HasComponent(entity)); Assert.IsTrue(EntityManager.HasComponent(entity)); -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS if (scheduleType == ScheduleType.Run) { Assert.IsTrue(EntityManager.HasComponent(entity)); diff --git a/Unity.Entities.Tests/ForEachCodegen/FixedWithSourceGenTests.cs b/Unity.Entities.Tests/ForEachCodegen/FixedWithSourceGenTests.cs index b11b741..4e338df 100644 --- a/Unity.Entities.Tests/ForEachCodegen/FixedWithSourceGenTests.cs +++ b/Unity.Entities.Tests/ForEachCodegen/FixedWithSourceGenTests.cs @@ -1,7 +1,3 @@ -#if !NET_DOTS -// NET_DOTS does not support TestCaseSource and these tests are only used to validate existing DOTS ILPP-related issues (not specifically NET_DOTS related) -// DOTS-3822 - using System; using System.Runtime.CompilerServices; using NUnit.Framework; @@ -308,5 +304,3 @@ protected override void OnUpdate() } } - -#endif diff --git a/Unity.Entities.Tests/ForEachCodegen/ForEachCodegenTests.cs b/Unity.Entities.Tests/ForEachCodegen/ForEachCodegenTests.cs index 6518f7c..3826b81 100644 --- a/Unity.Entities.Tests/ForEachCodegen/ForEachCodegenTests.cs +++ b/Unity.Entities.Tests/ForEachCodegen/ForEachCodegenTests.cs @@ -7,9 +7,7 @@ using Unity.Transforms; using UnityEngine; using Unity.Mathematics; -#if !UNITY_PORTABLE_TEST_RUNNER using System.Linq; -#endif namespace Unity.Entities.Tests.ForEachCodegen { @@ -280,9 +278,6 @@ public void CaptureAndOperateOnReferenceTypeTest() Assert.AreEqual("Hello hello hello", TestSystem.CaptureAndOperateOnReferenceType()); } -#if !UNITY_PORTABLE_TEST_RUNNER -// https://unity3d.atlassian.net/browse/DOTSR-1432 -// OrderBy isn't supported in DOTS-Runtime [Test] public void IterateSharedComponentDataTest() { @@ -301,8 +296,6 @@ public void IterateSharedComponentDataTest() Assert.AreEqual(2, sorted[1].Value); } -#endif - public struct MyBufferElementData : IBufferElementData { public int Value; @@ -389,7 +382,6 @@ public void ManyManagedComponents() #endif -#if !UNITY_DOTSRUNTIME [Test] public void UseUnityEngineComponent() { @@ -416,7 +408,6 @@ public void UnityEngineObjectAsWithParam() m_Manager.AddComponent(entity); TestSystem.UnityEngineObjectAsWithParam(); } -#endif #if ENABLE_UNITY_COLLECTIONS_CHECKS [Test] @@ -1223,7 +1214,6 @@ public void Many_ManagedComponents() #endif -#if !UNITY_DOTSRUNTIME public (Camera, Entity) IterateEntitiesWithCameraComponent() { (Camera camera, Entity entity)result = default; @@ -1254,7 +1244,6 @@ public void UnityEngineObjectAsWithParam() }).Run(); Assert.AreEqual(1, count); } -#endif public void RunForEachWithCustomDelegateTypeWithMoreThan8Parameters() { int grabbedData = -1; diff --git a/Unity.Entities.Tests/ForEachCodegen/ForEachISystemTests.cs b/Unity.Entities.Tests/ForEachCodegen/ForEachISystemTests.cs index 282ca9f..6713285 100644 --- a/Unity.Entities.Tests/ForEachCodegen/ForEachISystemTests.cs +++ b/Unity.Entities.Tests/ForEachCodegen/ForEachISystemTests.cs @@ -307,10 +307,9 @@ public void IterateExistingDynamicBufferReadOnly(ref SystemState systemState, bo public void ForEach_EntityParameter_NoWarnings(ref SystemState systemState) { systemState.Entities.ForEach((Entity entity) => {}).Run(); -#if !UNITY_DOTSRUNTIME LogAssert.NoUnexpectedReceived(); -#endif } + public void DisposeNativeArray(ref SystemState systemState, bool useSystemStateForEach) { var testArray = new NativeArray(100, Allocator.Temp); diff --git a/Unity.Entities.Tests/ForEachCodegen/ForEachWithDisposeOnJobCompletion.cs b/Unity.Entities.Tests/ForEachCodegen/ForEachWithDisposeOnJobCompletion.cs index 19e5557..a1e3b74 100644 --- a/Unity.Entities.Tests/ForEachCodegen/ForEachWithDisposeOnJobCompletion.cs +++ b/Unity.Entities.Tests/ForEachCodegen/ForEachWithDisposeOnJobCompletion.cs @@ -120,7 +120,6 @@ public enum ScheduleType } [Test] - [ManagedExceptionInPortableTests] [TestRequiresCollectionChecks("Requires Atomic Safety Handle for dispose checks")] public void DisposeOnCompletion_DisposesAtEnd([Values] ScheduleType scheduleType) { @@ -147,7 +146,6 @@ public void DisposeNativeArray_DisposesAtEnd() } [Test] - [ManagedExceptionInPortableTests] [TestRequiresCollectionChecks("Requires Atomic Safety Handle for dispose checks")] public void DisposeInsideStructOnJobCompletion_DisposesAtEnd([Values] ScheduleType scheduleType) { @@ -165,7 +163,6 @@ public void DisposeInsideStructOnJobCompletion_DisposesAtEnd([Values] ScheduleTy } [Test] - [ManagedExceptionInPortableTests] [TestRequiresCollectionChecks("Requires Atomic Safety Handle for dispose checks")] public void DisposeInsideClassOnJobCompletion_WithRun_DisposesAtEnd() { @@ -183,7 +180,6 @@ public void DisposeInsideClassOnJobCompletion_WithRun_DisposesAtEnd() } [Test] - [ManagedExceptionInPortableTests] [TestRequiresCollectionChecks("Requires Atomic Safety Handle for dispose checks")] public void DisposeOnCompletion_WithStructuralChanges_Disposes() { diff --git a/Unity.Entities.Tests/ForEachCodegen/ForEachWithStructuralChangesTests.cs b/Unity.Entities.Tests/ForEachCodegen/ForEachWithStructuralChangesTests.cs index 7f29004..d54511b 100644 --- a/Unity.Entities.Tests/ForEachCodegen/ForEachWithStructuralChangesTests.cs +++ b/Unity.Entities.Tests/ForEachCodegen/ForEachWithStructuralChangesTests.cs @@ -9,9 +9,7 @@ using Unity.Transforms; using UnityEngine; using UnityEngine.TestTools; -#if !UNITY_PORTABLE_TEST_RUNNER using System.Linq; -#endif namespace Unity.Entities.Tests.ForEachWithStructuralChangesCodegen { @@ -144,7 +142,6 @@ public void SharedComponent_ModifiedEntities_VisibleFromInsideForEach() } [Test] - [IgnoreInPortableTests("Assert.Throws isn't supported; the test runner doesn't currently find the lambda function.")] [TestRequiresDotsDebugOrCollectionChecks("Test requires entity data access safety checks")] public void DestroyEntity_EntityOperations_ShouldThrowWhenRequired() { @@ -164,7 +161,6 @@ public void RemoveComponent_ModifiedEntity_VisibleFromInsideForEach() } [Test] - [IgnoreInPortableTests("Assert.Throws isn't supported; the test runner doesn't currently find the lambda function.")] [TestRequiresDotsDebugOrCollectionChecks("Test requires entity data access safety checks")] public void RemoveComponent_GetOrSetOfRemovedComponent_Throws() { diff --git a/Unity.Entities.Tests/ForEachCodegen/JobEntityCodeGenTests.cs b/Unity.Entities.Tests/ForEachCodegen/JobEntityCodeGenTests.cs index 56b201c..ebb8df5 100644 --- a/Unity.Entities.Tests/ForEachCodegen/JobEntityCodeGenTests.cs +++ b/Unity.Entities.Tests/ForEachCodegen/JobEntityCodeGenTests.cs @@ -196,7 +196,6 @@ public void MultipleInNestedUsing([Values] ScheduleType scheduleTypeAssign, [Val #region ManagedComponents #if !UNITY_DISABLE_MANAGED_COMPONENTS -#if !UNITY_DOTSRUNTIME [Test] public void UnityEngineComponent() => m_TestSystem.UnityEngineComponent(); @@ -205,7 +204,6 @@ public void MultipleInNestedUsing([Values] ScheduleType scheduleTypeAssign, [Val [Test] public void UnityEngineScriptableObject() => m_TestSystem.UnityEngineScriptableObject(); -#endif [Test] public void ManyManagedComponents() => m_TestSystem.ManyManagedComponents(); #endif @@ -231,6 +229,11 @@ public void MultipleInNestedUsing([Values] ScheduleType scheduleTypeAssign, [Val #endregion +#region DuplicateComponents + [Test] + public void DuplicateComponents([Values(ScheduleType.Run, ScheduleType.Schedule)] ScheduleType scheduleType) => m_TestSystem.DuplicateComponents(scheduleType); +#endregion + #region EntityIndexInQuery_ArrayWrites [Test] @@ -1470,7 +1473,6 @@ public void ChunkIndexInQuery(ScheduleType scheduleType) { #region ManagedComponents #if !UNITY_DISABLE_MANAGED_COMPONENTS -#if !UNITY_DOTSRUNTIME public partial struct UnityEngineComponentJob : IJobEntity { void Execute(Transform transform) => transform.position = Vector3.up; @@ -1517,7 +1519,6 @@ public void UnityEngineScriptableObject() new UnityEngineScriptableObjectJob().Run(); Assert.AreEqual(so.value, 1); } -#endif public partial struct ManyManagedComponentsJob : IJobEntity { void Execute(EcsTestManagedComponent t0, EcsTestManagedComponent2 t1, EcsTestManagedComponent3 t2, EcsTestManagedComponent4 t3) @@ -1677,6 +1678,54 @@ public void EnableableComponents(ScheduleType scheduleType) #endregion +#region DuplicateComponents + + [WithOptions(EntityQueryOptions.IgnoreComponentEnabledState)] + partial struct DuplicateComponentsJob : IJobEntity + { + public void Execute(RefRW e1, EnabledRefRW e2) + { + if (e2.ValueRO) + { + e1.ValueRW.value++; + e2.ValueRW = false; + } + } + } + public void DuplicateComponents(ScheduleType scheduleType) + { + using var entities = CollectionHelper.CreateNativeArray(100, World.UpdateAllocator.ToAllocator); + EntityManager.CreateEntity(EntityManager.CreateArchetype(typeof(EcsTestDataEnableable)), entities); + + for (int i = 0; i < entities.Length; i+=2) + EntityManager.SetComponentEnabled(entities[i], false); + + var job = new DuplicateComponentsJob(); + switch (scheduleType) + { + case ScheduleType.Run: + job.Run(); + break; + case ScheduleType.Schedule: + job.Schedule(); + Dependency.Complete(); + break; + } + + for (var index = 0; index < entities.Length; index++) + { + var entity = entities[index]; + + Assert.IsFalse(EntityManager.IsComponentEnabled(entity)); + Assert.AreEqual(index % 2 == 0 ? 0 : 1, EntityManager.GetComponentData(entity).value); + } + + EntityManager.DestroyEntity(entities); + } + +#endregion + + #region EntityIndexInQuery_ArrayWrites partial struct EntityIndexInQuery_WriteToArray_Job : IJobEntity diff --git a/Unity.Entities.Tests/ForEachCodegen/JobEntityISystemTests.cs b/Unity.Entities.Tests/ForEachCodegen/JobEntityISystemTests.cs index ed29c72..92d3040 100644 --- a/Unity.Entities.Tests/ForEachCodegen/JobEntityISystemTests.cs +++ b/Unity.Entities.Tests/ForEachCodegen/JobEntityISystemTests.cs @@ -94,6 +94,9 @@ ref SystemState GetSystemStateRef() [Test] public void WithAbsent() => GetTestSystemUnsafe().WithAbsent(ref GetSystemStateRef()); + [Test] + public void WithPresent() => GetTestSystemUnsafe().WithPresent(ref GetSystemStateRef()); + [Test] public void WithAny_DoesntExecute_OnEntityWithoutThatComponent() => GetTestSystemUnsafe().WithAny_DoesntExecute_OnEntityWithoutThatComponent(ref GetSystemStateRef()); @@ -231,6 +234,14 @@ partial struct WithAbsentJob : IJobEntity void Execute(ref EcsTestData e1) => e1.value = absentValue; } + [WithPresent(typeof(EcsTestDataEnableable))] + partial struct WithPresentJob : IJobEntity + { + public int presentValue; + + void Execute(ref EcsTestData e1) => e1.value = presentValue; + } + [WithAny(typeof(EcsTestData3))] partial struct WithAny_DoesntExecute_OnEntityWithoutThatComponentJob : IJobEntity { @@ -365,6 +376,20 @@ public void WithAbsent(ref SystemState state) Assert.AreEqual(1, state.EntityManager.GetComponentData(JobEntityISystemTests.TestEntity).value); } + public void WithPresent(ref SystemState state) + { + // Test with the present component disabled + state.EntityManager.SetComponentEnabled(JobEntityISystemTests.TestEntity, false); + new WithPresentJob{ presentValue= 1}.Schedule(); + state.Dependency.Complete(); + Assert.AreEqual(1, state.EntityManager.GetComponentData(JobEntityISystemTests.TestEntity).value); + // test again with the present component enabled + state.EntityManager.SetComponentEnabled(JobEntityISystemTests.TestEntity, true); + new WithPresentJob{ presentValue= 2}.Schedule(); + state.Dependency.Complete(); + Assert.AreEqual(2, state.EntityManager.GetComponentData(JobEntityISystemTests.TestEntity).value); + } + public void WithAny_DoesntExecute_OnEntityWithoutThatComponent(ref SystemState state) { new WithAny_DoesntExecute_OnEntityWithoutThatComponentJob { one = 1 }.Schedule(); @@ -483,9 +508,7 @@ public void IJobEntity_EntityParameter_NoWarnings(ref SystemState state) { new SimpleEntityJob().Schedule(); state.Dependency.Complete(); -#if !UNITY_DOTSRUNTIME LogAssert.NoUnexpectedReceived(); -#endif } public void Schedule_CombineDependencies_Works(ref SystemState state) diff --git a/Unity.Entities.Tests/GenericJobScanningRepros.cs b/Unity.Entities.Tests/GenericJobScanningRepros.cs index fdb93c2..f06cddc 100644 --- a/Unity.Entities.Tests/GenericJobScanningRepros.cs +++ b/Unity.Entities.Tests/GenericJobScanningRepros.cs @@ -8,11 +8,8 @@ using UnityEngine; using UnityEngine.TestTools; using Unity.Transforms; -using Assert = UnityEngine.Assertions.Assert; - -#if !NET_DOTS // Regex is not supported in NET_DOTS using System.Text.RegularExpressions; -#endif +using Assert = UnityEngine.Assertions.Assert; // This file contains two user examples of generic jobs that we are trying to keep supporting with generic job type detection. // Please leave them here. @@ -252,11 +249,6 @@ public void Execute(Entity entity, ref Bar t0, ref Baz baz, NativeArray foo } } - // This should work in !NET_DOTS with UNITY_DOTSRUNTIME just fine, but that - // actually happening is currently WIP - so disable completely in UNITY_DOTSRUNTIME - // temporarily. -#if !UNITY_DOTSRUNTIME // Regex is not supported in NET_DOTS (and this is totally broken in DOTS Runtime temporarily) - //#if !NET_DOTS // Regex is not supported in NET_DOTS public class UserGenericJobCode1 : ECSTestsFixture { [Test] @@ -284,10 +276,9 @@ public void NoReflectionDataForHiddenGenericsInBurst() LogAssert.NoUnexpectedReceived(); } } -#endif - } +} - namespace Unity.Entities.Tests.CustomerProvided.Forum2 +namespace Unity.Entities.Tests.CustomerProvided.Forum2 { public struct Foo : IComponentData { @@ -338,7 +329,7 @@ protected override void OnUpdate() public class UserGenericJobCode2 : ECSTestsFixture { - [Test, DotsRuntimeFixme] + [Test] public void DoesntCrashEditor() { var repro = World.GetOrCreateSystemManaged(); diff --git a/Unity.Entities.Tests/IJobChunkTests.cs b/Unity.Entities.Tests/IJobChunkTests.cs index 05e1e83..22c1164 100644 --- a/Unity.Entities.Tests/IJobChunkTests.cs +++ b/Unity.Entities.Tests/IJobChunkTests.cs @@ -6,9 +6,7 @@ using Unity.Jobs; using UnityEngine; using UnityEngine.TestTools; -#if !NET_DOTS using System.Text.RegularExpressions; -#endif namespace Unity.Entities.Tests { @@ -29,11 +27,6 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE } } - static unsafe bool IsChunkInitialized(ArchetypeChunk chunk) - { - return chunk.m_Chunk != null; - } - // Not Burst compiling since we are Asserting in this job struct WriteChunkInfoToArray : IJobChunk { @@ -48,7 +41,7 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE { // We expect the Chunks array to be uninitialized until written by this job. // If this fires, some other job thread has filled in this batch's info already! - Assert.IsFalse(IsChunkInitialized(Chunks[unfilteredChunkIndex])); + Assert.IsTrue(Chunks[unfilteredChunkIndex].Invalid()); Assert.NotZero(chunk.Count); Chunks[unfilteredChunkIndex] = chunk; @@ -93,6 +86,57 @@ public void IJobChunk_RunWithoutDependency_Throws() } #endif + struct ChunkIndexJob : IJobChunk + { + public NativeArray OutPerChunkData; + public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) + { + // If JobsUtility.PatchBufferMinMaxRanges() is not called correctly, this array write will fail + OutPerChunkData[unfilteredChunkIndex] = unfilteredChunkIndex; + } + } + + [Test,TestRequiresCollectionChecks] + public void IJobChunk_DefaultArrayRangeCheck_Works([Values] bool enableChunkFilter) + { + var archetype = m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestSharedComp)); + int entityCount = 1000; + using var entities = m_Manager.CreateEntity(archetype, entityCount, World.UpdateAllocator.ToAllocator); + using var queryBuilder = new EntityQueryBuilder(Allocator.Temp).WithAll(); + using var query = m_Manager.CreateEntityQuery(queryBuilder); + int sharedComponentFilterValue = 17; + if (enableChunkFilter) + { + query.SetSharedComponentFilter(new EcsTestSharedComp { value = sharedComponentFilterValue }); + for (int i = 0; i < entityCount; i += 2) + { + m_Manager.SetSharedComponent(entities[i], + new EcsTestSharedComp { value = sharedComponentFilterValue }); + } + } + + int chunkCount = query.CalculateChunkCountWithoutFiltering(); + var outputPerChunkData = CollectionHelper.CreateNativeArray(chunkCount, World.UpdateAllocator.ToAllocator); + for (int i = 0; i < chunkCount; ++i) + outputPerChunkData[i] = -1; + var job = new ChunkIndexJob{ + OutPerChunkData = outputPerChunkData, + }; + job.ScheduleParallelByRef(query, default).Complete(); + + query.ResetFilter(); + using var unfilteredChunks = query.ToArchetypeChunkArray(Allocator.TempJob); + var sharedComponentTypeHandle = m_Manager.GetSharedComponentTypeHandle(); + for (int i = 0; i < chunkCount; ++i) + { + int expectedValue = i; + if (enableChunkFilter) + expectedValue = unfilteredChunks[i].GetSharedComponent(sharedComponentTypeHandle).value == sharedComponentFilterValue ? i : -1; + Assertions.Assert.AreEqual(expectedValue, outputPerChunkData[i]); + } + outputPerChunkData.Dispose(); + } + struct ChunkBaseEntityIndexJob : IJobChunk { [ReadOnly] public NativeArray ChunkBaseEntityIndices; @@ -112,7 +156,7 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE } } - [Test] + [Test,TestRequiresCollectionChecks] public void IJobChunk_OptionalArrayRangeCheck_Works() { var archetype = m_Manager.CreateArchetype(typeof(EcsTestData)); @@ -139,8 +183,6 @@ public enum ScheduleMode Parallel, Single, Run, RunWithoutJobs } -// TODO(https://unity3d.atlassian.net/browse/DOTSR-2746): [TestCase(args)] is not supported in the portable test runner -#if !UNITY_PORTABLE_TEST_RUNNER [TestCase(ScheduleMode.Parallel)] [TestCase(ScheduleMode.Single)] [TestCase(ScheduleMode.Run)] @@ -184,7 +226,7 @@ public void IJobChunk_WithoutFiltering_ExecutesOnExpectedChunks(ScheduleMode mod for (int chunkIndex = 0; chunkIndex < chunks.Length; ++chunkIndex) { var chunk = chunks[chunkIndex]; - if (!IsChunkInitialized(chunk)) + if (chunk.Invalid()) continue; // this is fine; empty/filtered batches will be skipped and left uninitialized. FastAssert.Greater(chunk.Count, 0); // empty batches should not have been Execute()ed FastAssert.AreEqual(chunk.Count, chunk.Count); @@ -254,7 +296,7 @@ public void IJobChunk_WithFiltering_ExecutesOnExpectedChunks(ScheduleMode mode) for (int chunkIndex = 0; chunkIndex < chunks.Length; ++chunkIndex) { var chunk = chunks[chunkIndex]; - if (!IsChunkInitialized(chunk)) + if (chunk.Invalid()) continue; // this is fine; empty/filtered batches will be skipped and left uninitialized. FastAssert.Greater(chunk.Count, 0); // empty batches should not have been Execute()ed FastAssert.AreEqual(chunk.Count, chunk.Count); @@ -284,7 +326,6 @@ public void IJobChunk_WithFiltering_ExecutesOnExpectedChunks(ScheduleMode mode) } } } -#endif [BurstCompile(CompileSynchronously = true)] struct WriteToArray : IJobChunk @@ -300,7 +341,6 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE } } -#if !NET_DOTS // DOTS Runtimes does not support regex [Test] [TestRequiresCollectionChecks("Requires Job Safety System")] public void ParallelArrayWriteTriggersSafetySystem() @@ -318,7 +358,6 @@ public void ParallelArrayWriteTriggersSafetySystem() }.ScheduleParallel(query, default).Complete(); } } -#endif [Test] public void SingleArrayWriteDoesNotTriggerSafetySystem() @@ -559,8 +598,6 @@ public void Execute() } #if ENABLE_UNITY_COLLECTIONS_CHECKS - // TODO(DOTS-6573): This test can be enabled once DOTSRT supports AtomicSafetyHandle.SetExclusiveWeak() -#if !UNITY_DOTSRUNTIME [Test] public void ConcurrentJob_WritesToEnableableComponentInQuery_Throws() { @@ -576,7 +613,6 @@ public void ConcurrentJob_WritesToEnableableComponentInQuery_Throws() // With the correct dependency, it's fine. Assert.DoesNotThrow(() => job2.Schedule(handle1).Complete()); } -#endif #endif } diff --git a/Unity.Entities.Tests/IdiomaticCSharpForEachIterationTests.cs b/Unity.Entities.Tests/IdiomaticCSharpForEachIterationTests.cs index 540f4ac..a46500f 100644 --- a/Unity.Entities.Tests/IdiomaticCSharpForEachIterationTests.cs +++ b/Unity.Entities.Tests/IdiomaticCSharpForEachIterationTests.cs @@ -25,6 +25,7 @@ public enum QueryExtension None, Disabled, Absent, + Present, NoExtension } @@ -121,6 +122,7 @@ struct EnableableAnyTag : IComponentData, IEnableableComponent {} struct EnableableNoneTag : IComponentData, IEnableableComponent {} struct EnableableDisabledTag : IComponentData, IEnableableComponent {} struct EnableableAbsentTag : IComponentData, IEnableableComponent {} + struct EnableablePresentTag : IComponentData, IEnableableComponent {} partial class IterateThroughEnableableComponents_TrackProcessedEntities : SystemBase { public NativeList ProcessedEntities; @@ -142,6 +144,7 @@ protected override void OnUpdate() .WithNone() .WithDisabled() .WithAbsent() + .WithPresent() .WithEntityAccess()) { ProcessedEntities.Add(entity); @@ -149,11 +152,58 @@ protected override void OnUpdate() } } + partial struct IterateThroughEnableableComponentsWithVariousExtensionsSystem : ISystem + { + public void OnCreate(ref SystemState state) + { + state.EntityManager.AddComponent(state.SystemHandle); + state.EntityManager.AddComponent(state.SystemHandle); + + var archetype = state.EntityManager.CreateArchetype(typeof(EcsTestDataEnableable)); + const int entityCount = 1000; + var allEntities = state.EntityManager.CreateEntity(archetype, entityCount, Allocator.Temp); + + for (int i = 0; i < entityCount; ++i) + state.EntityManager.SetComponentEnabled(allEntities[i], i % 5 != 0); + } + + public void OnUpdate(ref SystemState state) + { + var queryExtensionToTest = state.EntityManager.GetComponentData(state.SystemHandle).extensionToTest; + ref var numDisabledComponents = ref state.EntityManager.GetComponentDataRW(state.SystemHandle).ValueRW; + + switch (queryExtensionToTest) + { + case QueryExtension.Disabled: + foreach (var _ in Query>().WithDisabled()) + numDisabledComponents.sum++; + break; + case QueryExtension.None: + foreach (var _ in Query>().WithNone()) + numDisabledComponents.sum++; + break; + case QueryExtension.Any: + foreach (var _ in Query>().WithAny()) + numDisabledComponents.sum++; + break; + case QueryExtension.All: + foreach (var _ in Query>().WithAll()) + numDisabledComponents.sum++; + break; + } + } + } + public struct SumData : IComponentData { public int sum; } + public struct SharedComponentFilterData : IComponentData + { + public bool IsManaged; + } + partial class IterateThroughComponent_WithEnableableComponent_EnableSystem : SystemBase { public Entity Entity { get; private set; } @@ -292,12 +342,21 @@ partial struct IterateThroughComponent_WithSharedComponentFilterSystem : ISystem public void OnCreate(ref SystemState state) { state.EntityManager.AddComponent(state.SystemHandle); + state.EntityManager.AddComponent(state.SystemHandle); } + public void OnUpdate(ref SystemState state) { + var sharedComponentFilterData = state.EntityManager.GetComponentData(state.SystemHandle); ref var sumData = ref state.EntityManager.GetComponentDataRW(state.SystemHandle).ValueRW; - foreach (var ecsTestData in Query>().WithSharedComponentFilter(new SharedData1(1))) - sumData.sum += ecsTestData.ValueRO.value; + + if (sharedComponentFilterData.IsManaged) + foreach (var result in Query>().WithSharedComponentFilterManaged(new ManagedSharedData1(val: 1))) + sumData.sum += result.ValueRO.value; + else + foreach (var result in Query>().WithSharedComponentFilter(new SharedData1(1))) + sumData.sum += result.ValueRO.value; + } } @@ -486,6 +545,17 @@ public void OnUpdate(ref SystemState state) ecsTestData3.ValueRW.value2 = 30; } break; + case QueryExtension.Present: + foreach (var (myAspect, ecsTestData3) in Query>().WithPresent().WithOptions(EntityQueryOptions.IncludeSystems)) + { + myAspect._Data.ValueRW = new EcsTestData { value = 10 }; + myAspect._Data2.ValueRW = new EcsTestData2 { value0 = 20, value1 = 30 }; + + ecsTestData3.ValueRW.value0 = 10; + ecsTestData3.ValueRW.value1 = 20; + ecsTestData3.ValueRW.value2 = 30; + } + break; } } } @@ -1049,6 +1119,12 @@ public void ForEachIteration_WithEnableableComponents_ProcessCorrectEntities() if (i % 2 == 0) m_Manager.SetComponentEnabled(e, false); } + if ((i / 19) % 2 == 0) + { + m_Manager.AddComponent(e); + if (i % 2 == 0) + m_Manager.SetComponentEnabled(e, false); + } } using var query = new EntityQueryBuilder(Allocator.Temp) @@ -1057,6 +1133,7 @@ public void ForEachIteration_WithEnableableComponents_ProcessCorrectEntities() .WithNone() .WithDisabled() .WithAbsent() + .WithPresent() .Build(m_Manager); using var expectedEntities = query.ToEntityArray(Allocator.Temp); @@ -1065,6 +1142,29 @@ public void ForEachIteration_WithEnableableComponents_ProcessCorrectEntities() CollectionAssert.AreEqual(expectedEntities.ToArray(), sys.ProcessedEntities.AsArray().ToArray()); } + [Test] + public void ForEachIteration_IterateThroughEnableableComponents( + [Values(QueryExtension.None, QueryExtension.Disabled, QueryExtension.Any, QueryExtension.All)] QueryExtension queryExtension) + { + var system = World.GetOrCreateSystem(); + ref var queryData = ref World.EntityManager.GetComponentDataRW(system).ValueRW; + queryData.extensionToTest = queryExtension; + + system.Update(World.Unmanaged); + + switch (queryExtension) + { + case QueryExtension.Any: + case QueryExtension.All: + Assert.AreEqual(expected: 800, actual: World.EntityManager.GetComponentData(system).sum); + break; + case QueryExtension.None: + case QueryExtension.Disabled: + Assert.AreEqual(expected: 200, actual: World.EntityManager.GetComponentData(system).sum); + break; + } + } + [Test] public void ForEachIteration_IterateThroughTagComponent() => World.GetOrCreateSystem().Update(World.Unmanaged); @@ -1089,24 +1189,37 @@ public void ForEachIteration_ThroughValueTypeComponentWithoutRefRO() } [Test] - public void ForEachIteration_ThroughComponent_WithSharedComponentFilter() + public void ForEachIteration_ThroughComponent_WithSharedComponentFilter([Values] bool isManaged) { // Creating entities here instead of inside `IterateThroughComponent_WithSharedComponentFilterSystem.OnCreate()` // in order to circumvent a bug in `EntityManagerDebug.CheckInternalConsistency()`: // See: https://unity.slack.com/archives/C01HYKXLM42/p1654006277589939 var entity0 = World.EntityManager.CreateEntity(typeof(EcsTestData)); World.EntityManager.SetComponentData(entity0, new EcsTestData(1)); - World.EntityManager.AddSharedComponentManaged(entity0, new SharedData1(1)); + if (isManaged) + World.EntityManager.AddSharedComponentManaged(entity0, new ManagedSharedData1(1)); + else + World.EntityManager.AddSharedComponent(entity0, new SharedData1(1)); var entity1 = World.EntityManager.CreateEntity(typeof(EcsTestData)); World.EntityManager.SetComponentData(entity1, new EcsTestData(10)); - World.EntityManager.AddSharedComponentManaged(entity1, new SharedData1(1)); + if (isManaged) + World.EntityManager.AddSharedComponentManaged(entity1, new ManagedSharedData1(1)); + else + World.EntityManager.AddSharedComponent(entity1, new SharedData1(1)); var entity2 = World.EntityManager.CreateEntity(typeof(EcsTestData)); World.EntityManager.SetComponentData(entity2, new EcsTestData(100)); - World.EntityManager.AddSharedComponentManaged(entity2, new SharedData1(2)); + if (isManaged) + World.EntityManager.AddSharedComponentManaged(entity2, new ManagedSharedData1(2)); + else + World.EntityManager.AddSharedComponent(entity2, new SharedData1(2)); var system = World.GetOrCreateSystem(); + + ref var sharedComponentFilterData = ref World.EntityManager.GetComponentDataRW(system).ValueRW; + sharedComponentFilterData.IsManaged = isManaged; + system.Update(World.Unmanaged); Assert.AreEqual(11, World.EntityManager.GetComponentData(system).sum); @@ -1124,15 +1237,15 @@ public void TwoForEachIterationsWithSameBackingQuery_OneWithSharedComponentFilte // See: https://unity.slack.com/archives/C01HYKXLM42/p1654006277589939 var entity0 = World.EntityManager.CreateEntity(typeof(EcsTestData)); World.EntityManager.SetComponentData(entity0, new EcsTestData(1)); - World.EntityManager.AddSharedComponentManaged(entity0, new SharedData1(1)); + World.EntityManager.AddSharedComponent(entity0, new SharedData1(1)); var entity1 = World.EntityManager.CreateEntity(typeof(EcsTestData)); World.EntityManager.SetComponentData(entity1, new EcsTestData(10)); - World.EntityManager.AddSharedComponentManaged(entity1, new SharedData1(1)); + World.EntityManager.AddSharedComponent(entity1, new SharedData1(1)); var entity2 = World.EntityManager.CreateEntity(typeof(EcsTestData)); World.EntityManager.SetComponentData(entity2, new EcsTestData(100)); - World.EntityManager.AddSharedComponentManaged(entity2, new SharedData1(2)); + World.EntityManager.AddSharedComponent(entity2, new SharedData1(2)); var system = World.GetOrCreateSystem(); system.Update(World.Unmanaged); diff --git a/Unity.Entities.Tests/IterationTests.cs b/Unity.Entities.Tests/IterationTests.cs index 550d392..564b919 100644 --- a/Unity.Entities.Tests/IterationTests.cs +++ b/Unity.Entities.Tests/IterationTests.cs @@ -274,7 +274,6 @@ public void EntityQueryFilteredChunkCount() } #if !UNITY_DISABLE_MANAGED_COMPONENTS -#if !UNITY_PORTABLE_TEST_RUNNER // Does not support managed components. [Test] public void CreateEntityQuery_ManagedComponents() { @@ -505,7 +504,6 @@ public void EntityQueryFilteredChunkCount_ManagedComponents() query.Dispose(); } -#endif #endif } } diff --git a/Unity.Entities.Tests/JobBasicTests.cs b/Unity.Entities.Tests/JobBasicTests.cs index bb6589d..93f48e0 100644 --- a/Unity.Entities.Tests/JobBasicTests.cs +++ b/Unity.Entities.Tests/JobBasicTests.cs @@ -55,11 +55,6 @@ public void Execute() { for (int i = 0; i < N; ++i) result[i] = a + b; - -#if UNITY_DOTSRUNTIME && ENABLE_UNITY_COLLECTIONS_CHECKS // TODO: Don't have the library in the editor that grants access. - AssertOnThread(result.m_Safety.IsAllowedToWrite()); - AssertOnThread(!result.m_Safety.IsAllowedToRead()); -#endif } } @@ -117,16 +112,6 @@ public struct SimpleAddSerial : IJob public void Execute() { -#if UNITY_DOTSRUNTIME && ENABLE_UNITY_COLLECTIONS_CHECKS // Don't have the C# version in the editor. - AssertOnThread(!input.m_Safety.IsAllowedToWrite()); - AssertOnThread(input.m_Safety.IsAllowedToRead()); - AssertOnThread(result.m_Safety.IsAllowedToWrite()); - AssertOnThread(!result.m_Safety.IsAllowedToRead()); - -#if UNITY_SINGLETHREADED_JOBS - AssertOnThread(JobsUtility.IsExecutingJob); -#endif -#endif for (int i = 0; i < N; ++i) result[i] = a + input[i]; } @@ -135,10 +120,6 @@ public void Execute() [Test] public void Run3SimpleJobsInSerial() { -#if UNITY_DOTSRUNTIME - // Note the safety handles use Persistent, so only track TempJob - long heapMem = UnsafeUtility.GetHeapSize(Allocator.TempJob); -#endif NativeArray input = CollectionHelper.CreateNativeArray(SimpleAddSerial.N, ref World.UpdateAllocator); NativeArray jobResult1 = CollectionHelper.CreateNativeArray(SimpleAddSerial.N, ref World.UpdateAllocator); NativeArray jobResult2 = CollectionHelper.CreateNativeArray(SimpleAddSerial.N, ref World.UpdateAllocator); @@ -166,11 +147,6 @@ public void Run3SimpleJobsInSerial() { Assert.AreEqual(i + 1 + 2 + 3, jobResult3[i]); } - -#if UNITY_DOTSRUNTIME - long postWork = UnsafeUtility.GetHeapSize(Allocator.TempJob); - Assert.IsTrue(heapMem == postWork); // make sure cleanup happened, including DeallocateOnJobCompletion -#endif } public struct SimpleAddParallel : IJob @@ -238,12 +214,6 @@ public struct SimpleListAdd : IJob public void Execute() { -#if UNITY_DOTSRUNTIME && ENABLE_UNITY_COLLECTIONS_CHECKS // Don't have the C# version in the editor. - AssertOnThread(!input.m_Safety.IsAllowedToWrite()); - AssertOnThread(input.m_Safety.IsAllowedToRead()); - AssertOnThread(result.m_Safety.IsAllowedToWrite()); - AssertOnThread(!result.m_Safety.IsAllowedToRead()); -#endif for (int i = 0; i < N; ++i) result.Add(a + input[i]); } @@ -300,14 +270,6 @@ public struct SimpleParallelFor : IJobParallelFor public void Execute(int i) { -#if UNITY_DOTSRUNTIME && ENABLE_UNITY_COLLECTIONS_CHECKS // Don't have the C# version in the editor. - AssertOnThread(!a.m_Safety.IsAllowedToWrite()); - AssertOnThread(a.m_Safety.IsAllowedToRead()); - AssertOnThread(!b.m_Safety.IsAllowedToWrite()); - AssertOnThread(b.m_Safety.IsAllowedToRead()); - AssertOnThread(result.m_Safety.IsAllowedToWrite()); - AssertOnThread(!result.m_Safety.IsAllowedToRead()); -#endif result[i] = a[i] + b[i]; } } @@ -622,10 +584,6 @@ public struct HashWriterJob : IJob public void Execute() { -#if UNITY_DOTSRUNTIME && ENABLE_UNITY_COLLECTIONS_CHECKS // Don't have the C# version in the editor. - Assert.IsTrue(result.m_Safety.IsAllowedToWrite()); - Assert.IsTrue(!result.m_Safety.IsAllowedToRead()); -#endif for (int i = 0; i < N; ++i) { result.TryAdd(i, 47); diff --git a/Unity.Entities.Tests/JobSafetyTests.cs b/Unity.Entities.Tests/JobSafetyTests.cs index 04ec22a..42c0db3 100644 --- a/Unity.Entities.Tests/JobSafetyTests.cs +++ b/Unity.Entities.Tests/JobSafetyTests.cs @@ -55,10 +55,6 @@ public void ComponentAccessAfterScheduledJobThrows() } #endif - // These tests require: - // - JobsDebugger support for static safety IDs (added in 2020.1) - // - Asserting throws -#if !UNITY_DOTSRUNTIME struct UseComponentLookup : IJob { public ComponentLookup data; @@ -67,7 +63,7 @@ public void Execute() } } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Requires Job Safety System")] public void ComponentLookup_UseAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -82,7 +78,7 @@ public void ComponentLookup_UseAfterStructuralChange_ThrowsCustomErrorMessage() "ComponentLookup which has been invalidated by a structural change")); } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Requires Job Safety System")] public void ComponentLookup_UseFromJobAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -108,7 +104,7 @@ public void Execute() } } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Requires Job Safety System")] public void BufferLookup_UseAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -123,7 +119,7 @@ public void BufferLookup_UseAfterStructuralChange_ThrowsCustomErrorMessage() "BufferLookup which has been invalidated by a structural change.")); } - [Test,DotsRuntimeFixme] + [Test] [TestRequiresCollectionChecks("Requires Job Safety System")] public void BufferLookup_UseFromJobAfterStructuralChange_ThrowsCustomErrorMessage() { @@ -141,8 +137,6 @@ public void BufferLookup_UseFromJobAfterStructuralChange_ThrowsCustomErrorMessag "BufferLookup UseBufferLookup.data which has been invalidated by a structural change.")); } -#endif - [Test] public void GetComponentCompletesJob() { @@ -225,9 +219,7 @@ private void CleanupWorld() [TestRequiresCollectionChecks("Requires Job Safety System")] public void EntityManagerDestructionDetectsUnregisteredJob() { -#if !NET_DOTS LogAssert.Expect(LogType.Error, new System.Text.RegularExpressions.Regex("job is still running")); -#endif /*var entity =*/ m_Manager.CreateEntity(typeof(EcsTestData)); var query = m_Manager.CreateEntityQuery(typeof(EcsTestData)); @@ -243,9 +235,7 @@ public void EntityManagerDestructionDetectsUnregisteredJob() // Manually complete the job before cleaning up for real jobHandle.Complete(); CleanupWorld(); -#if !NET_DOTS LogAssert.NoUnexpectedReceived(); -#endif } [Test] @@ -417,7 +407,6 @@ public void JobsUsingArchetypeChunkSharedComponentTypeSyncOnStructuralChange() m_Manager.DestroyEntity(entity); } -#if !UNITY_DOTSRUNTIME partial struct BufferSafetyJobA : IJobEntity { public NativeArray MyArray; @@ -639,7 +628,6 @@ public void DynamicBuffer_UnsafePtr_DoesntThrowWhenReadWrite() SetupDynamicBufferJobTestEnvironment(); _testSystem.BufferSafetyJob_GetUnsafePtrReadWrite_Run(); } -#endif // !UNITY_DOTSRUNTIME public partial class DynamicBufferReadOnlySystem : SystemBase { diff --git a/Unity.Entities.Tests/Journaling/EntitiesJournalingTests.cs b/Unity.Entities.Tests/Journaling/EntitiesJournalingTests.cs index 61e1d43..1893e3d 100644 --- a/Unity.Entities.Tests/Journaling/EntitiesJournalingTests.cs +++ b/Unity.Entities.Tests/Journaling/EntitiesJournalingTests.cs @@ -913,7 +913,7 @@ public void AddChunkComponentData() m_Manager.AddChunkComponentData(entity); } - var chunkEntity = m_Manager.GetChunk(entity).m_Chunk->metaChunkEntity; + var chunkEntity = m_Manager.GetChunk(entity).m_Chunk.MetaChunkEntity; CheckRecords( new RecordDesc(0, RecordType.AddComponent, World, entities: ToArray(entity), componentTypes: ToArray(ComponentType.ChunkComponent())), new RecordDesc(1, RecordType.GetComponentDataRW, World, entities: ToArray(chunkEntity), componentTypes: ToArray(typeof(ChunkHeader)), data: ToArray(ChunkHeader.Null)) @@ -932,7 +932,7 @@ public void AddChunkComponentData_WithQuery() } } - var chunkEntity = m_Manager.GetChunk(entity).m_Chunk->metaChunkEntity; + var chunkEntity = m_Manager.GetChunk(entity).m_Chunk.MetaChunkEntity; CheckRecords( new RecordDesc(0, RecordType.AddComponent, World, entities: ToArray(entity), componentTypes: ToArray(ComponentType.ChunkComponent())), new RecordDesc(1, RecordType.GetComponentDataRW, World, entities: ToArray(chunkEntity), componentTypes: ToArray(typeof(ChunkHeader)), data: ToArray(ChunkHeader.Null)), @@ -1381,7 +1381,7 @@ public void SetChunkComponentData() m_Manager.SetChunkComponentData(chunk, new EcsTestData(42)); } - var chunkEntity = chunk.m_Chunk->metaChunkEntity; + var chunkEntity = chunk.m_Chunk.MetaChunkEntity; CheckRecords( new RecordDesc(0, RecordType.GetComponentDataRW, World, entities: ToArray(chunkEntity), componentTypes: ToArray(typeof(EcsTestData)), data: ToArray(new EcsTestData())) ); @@ -1597,7 +1597,6 @@ public void ConvertGetRWsToSets() #if !UNITY_ANDROID // APK bundling breaks reading from streamingAssets (DOTS-7038) [Test] - [IgnoreTest_IL2CPP("DOTSE-1903 - Properties is crashing due to generic interface usage breaking non-generic-sharing IL2CPP builds")] public void ExportToCSV() { using (var entities = m_Manager.CreateEntity(m_Manager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3)), 3, Allocator.Temp)) diff --git a/Unity.Entities.Tests/ManagedObjectRemapTests.cs b/Unity.Entities.Tests/ManagedObjectRemapTests.cs index a609432..d4e5c77 100644 --- a/Unity.Entities.Tests/ManagedObjectRemapTests.cs +++ b/Unity.Entities.Tests/ManagedObjectRemapTests.cs @@ -1,4 +1,4 @@ -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS using System.Collections.Generic; using System.Linq; using NUnit.Framework; diff --git a/Unity.Entities.Tests/ManagedObjectUtilityTests.cs b/Unity.Entities.Tests/ManagedObjectUtilityTests.cs index fa23cd8..366e87d 100644 --- a/Unity.Entities.Tests/ManagedObjectUtilityTests.cs +++ b/Unity.Entities.Tests/ManagedObjectUtilityTests.cs @@ -6,7 +6,7 @@ namespace Unity.Entities.Tests { -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS [TestFixture] sealed class ManagedObjectUtilityTests { diff --git a/Unity.Entities.Tests/MoveEntitiesFromTests.cs b/Unity.Entities.Tests/MoveEntitiesFromTests.cs index 6ed6c8c..c3ce761 100644 --- a/Unity.Entities.Tests/MoveEntitiesFromTests.cs +++ b/Unity.Entities.Tests/MoveEntitiesFromTests.cs @@ -658,12 +658,8 @@ public void MoveEntitiesVersionBumping([Values] bool useQuery) creationWorld.Dispose(); } -#if !NET_DOTS -// https://unity3d.atlassian.net/browse/DOTSR-1432 - #if !UNITY_DISABLE_MANAGED_COMPONENTS [Test] - [DotsRuntimeFixme] // No Unity.Properties Support [IgnoreTest_IL2CPP("DOTSE-1903 - Users Properties which is broken in non-generic sharing IL2CPP builds")] public void MoveEntitiesPatchesEntityReferences_ManagedComponents([Values] bool useFilteredMove) { @@ -722,7 +718,6 @@ public void MoveEntitiesPatchesEntityReferences_ManagedComponents([Values] bool } [Test] - [DotsRuntimeFixme] // No Unity.Properties Support [IgnoreTest_IL2CPP("DOTSE-1903 - Users Properties which is broken in non-generic sharing IL2CPP builds")] public void MoveEntitiesPatchesEntityReferencesInCollections_ManagedComponents() { @@ -789,8 +784,6 @@ public void MoveEntitiesPatchesEntityReferencesInCollections_ManagedComponents() sourceEntities.Dispose(); sourceWorld.Dispose(); } - -#endif #endif } } diff --git a/Unity.Entities.Tests/ProfilerModules/MemoryProfilerTests.cs b/Unity.Entities.Tests/ProfilerModules/MemoryProfilerTests.cs index 9d1563f..906d128 100644 --- a/Unity.Entities.Tests/ProfilerModules/MemoryProfilerTests.cs +++ b/Unity.Entities.Tests/ProfilerModules/MemoryProfilerTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Unity.Collections; using Unity.Profiling; using UnityEditor.Profiling; using UnityEditorInternal; @@ -139,7 +140,7 @@ public void Uninitialized_DoesNotThrow() [TestCase(typeof(EcsTestData), typeof(EcsTestData2))] [TestCase(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3))] [TestCase(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3), typeof(EcsTestData4))] -#if !UNITY_DOTSRUNTIME && !UNITY_WEBGL +#if !UNITY_WEBGL [ConditionalIgnore("IgnoreForCoverage", "Fails randonly when ran with code coverage enabled")] #endif public void ArchetypeOnly(params Type[] types) @@ -188,7 +189,7 @@ public void ArchetypeOnly(params Type[] types) [TestCase(100, typeof(EcsTestData), typeof(EcsTestData2))] [TestCase(1000, typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3))] [TestCase(10000, typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData3), typeof(EcsTestData4))] -#if !UNITY_DOTSRUNTIME && !UNITY_WEBGL +#if !UNITY_WEBGL [ConditionalIgnore("IgnoreForCoverage", "Fails randonly when ran with code coverage enabled")] #endif public void ArchetypeWithEntities(int entityCount, params Type[] types) diff --git a/Unity.Entities.Tests/ScratchpadAllocatorTests.cs b/Unity.Entities.Tests/ScratchpadAllocatorTests.cs index 6408143..300317d 100644 --- a/Unity.Entities.Tests/ScratchpadAllocatorTests.cs +++ b/Unity.Entities.Tests/ScratchpadAllocatorTests.cs @@ -1,4 +1,4 @@ -#if !UNITY_DOTSRUNTIME && ENABLE_UNITY_COLLECTIONS_CHECKS +#if ENABLE_UNITY_COLLECTIONS_CHECKS using System; using System.Text.RegularExpressions; diff --git a/Unity.Entities.Tests/Serialization/SerializeTests.cs b/Unity.Entities.Tests/Serialization/SerializeTests.cs index 26e0dbe..d8aa61a 100644 --- a/Unity.Entities.Tests/Serialization/SerializeTests.cs +++ b/Unity.Entities.Tests/Serialization/SerializeTests.cs @@ -17,9 +17,7 @@ using BinaryReader = Unity.Entities.Serialization.BinaryReader; using BinaryWriter = Unity.Entities.Serialization.BinaryWriter; using Random = Unity.Mathematics.Random; -#if !UNITY_PORTABLE_TEST_RUNNER using System.IO; -#endif namespace Unity.Entities.Tests { @@ -88,7 +86,6 @@ public void Execute() } } -#if !UNITY_PORTABLE_TEST_RUNNER internal class YAMLSerializationHelpers { /// @@ -118,7 +115,6 @@ public static bool EqualYAMLFiles(Stream fileA, Stream fileB) } } } -#endif #if UNITY_EDITOR public static class Hash128Helpers @@ -1592,10 +1588,8 @@ public unsafe void SerializeEntitiesWorksWithBlobAssetReferences() } #if ENABLE_UNITY_COLLECTIONS_CHECKS -#if !NET_DOTS // If memory has been unmapped this can throw exceptions other than InvalidOperation float f = 1.0f; Assert.Throws(() => f = arrayComponent.array.Value[0]); -#endif #endif } @@ -1784,10 +1778,8 @@ public unsafe void SerializeEntitiesWorksWithBlobAssetReferencesNoSharedComponen } #if ENABLE_UNITY_COLLECTIONS_CHECKS -#if !UNITY_DOTSRUNTIME // If memory has been unmapped this can throw exceptions other than InvalidOperation float f = 1.0f; Assert.Throws(() => f = arrayComponent.array.Value[0]); -#endif #endif } @@ -1798,7 +1790,6 @@ public unsafe struct ComponentWithPointer : IComponentData byte* m_Data; } -#if !UNITY_DOTSRUNTIME // We don't have reflection to validate if a type is serializable in NET_DOTS [Test] public void SerializeComponentWithPointerField() { @@ -1901,7 +1892,6 @@ public void EnsureSerializationWhitelistingDoesNotTrumpValidation() ); } } -#endif [Test] public void DeserializedChunksAreConsideredChangedOnlyOnce() @@ -2508,81 +2498,6 @@ public void SerializeEntitiesManagedComponentWithCustomClass_ManagedComponents() } } -#if UNITY_EDITOR - [Test] - public void WorldYamlSerializationTest() - { - var dummyEntity = CreateEntityWithDefaultData(0); //To ensure entity indices are offset - var e1 = CreateEntityWithDefaultData(1); - var e2 = CreateEntityWithDefaultData(2); - var e3 = CreateEntityWithDefaultData(3); - m_Manager.AddComponentData(e1, new TestComponentData1 { value = 10, referencedEntity = e2 }); - m_Manager.AddComponentData(e2, new TestComponentData2 { value = 20, referencedEntity = e1 }); - m_Manager.AddComponentData(e3, new TestComponentData1 { value = 30, referencedEntity = Entity.Null }); - m_Manager.AddComponentData(e3, new TestComponentData2 { value = 40, referencedEntity = Entity.Null }); - m_Manager.AddBuffer(e1); - m_Manager.RemoveComponent(e3); - m_Manager.AddBuffer(e3); - - m_Manager.GetBuffer(e1).CopyFrom(new EcsIntElement[] { 1, 2, 3 }); // no overflow - m_Manager.GetBuffer(e3).CopyFrom(new EcsIntElement[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }); // overflow into heap - - var e4 = m_Manager.CreateEntity(); - m_Manager.AddBuffer(e4); - var ebuf = m_Manager.GetBuffer(e4); - ebuf.Add(new EcsComplexEntityRefElement { Entity = e1, Dummy = 1 }); - ebuf.Add(new EcsComplexEntityRefElement { Entity = e2, Dummy = 2 }); - ebuf.Add(new EcsComplexEntityRefElement { Entity = e3, Dummy = 3 }); - - m_Manager.DestroyEntity(dummyEntity); - - var refFilePathName = @"Packages\com.unity.entities\Unity.Entities.Tests\Serialization\WorldTest.yaml"; - - // To generate the file we'll test against - using (var sw = new StreamWriter(refFilePathName)) - { - sw.NewLine = "\n"; - SerializeUtility.SerializeWorldIntoYAML(m_Manager, sw, false); - } - - using (var memStream = new MemoryStream()) - { - byte[] testContentBuffer; - - // Save the World to a memory buffer via a a Stream Writer - using (var sw = new StreamWriter(memStream)) - { - sw.NewLine = "\n"; - SerializeUtility.SerializeWorldIntoYAML(m_Manager, sw, false); - sw.Flush(); - memStream.Seek(0, SeekOrigin.Begin); - testContentBuffer = memStream.ToArray(); - } - - // Load both reference content and the test one into strings and compare - using (var sr = File.OpenRead(refFilePathName)) - using (var testMemoryStream = new MemoryStream(testContentBuffer)) - { - Assert.IsTrue(YAMLSerializationHelpers.EqualYAMLFiles(sr, testMemoryStream)); - } - } - } - - [Test] - public void WorldYamlSerialization_UsingStreamWriterWithCRLF_ThrowsArgumentException() - { - using (var memStream = new MemoryStream()) - using (var streamWriter = new StreamWriter(memStream)) - { - streamWriter.NewLine = "\r\n"; - Assert.Throws(() => - { - SerializeUtility.SerializeWorldIntoYAML(m_Manager, streamWriter, false); - }); - } - } - -#endif // UNITY_EDITOR #endif // !UNITY_DISABLE_MANAGED_COMPONENTS [Test] @@ -2681,6 +2596,138 @@ public void SerializeEntities_WithUnmanagedSharedComponents_Works() CollectionAssert.AreEquivalent(new[] {default(TestUnmanagedStruct), s1, s2}, sharedComponents.AsArray().ToArray(), "The shared component values are not equal"); } + struct TestUnmanagedBlobStruct : ISharedComponentData + { + public int Value; + public BlobAssetReference> BlobAssetReference; + } + + BlobAssetReference> CreateBlobArrayRef(float[] values) + { + var builder1 = new BlobBuilder(Allocator.Temp); + ref var blobArray = ref builder1.ConstructRoot>(); + var array1 = builder1.Allocate(ref blobArray, values.Length); + for (int index = 0; index < values.Length; index++) + array1[index] = values[index]; + var reference = builder1.CreateBlobAssetReference>(Allocator.Temp); + builder1.Dispose(); + return reference; + } + + [Test] + public void SerializeEntities_WithUnmanagedSharedComponentsDifferentBlob_Works() + { + float[] array1 = {1.7f, 2.6f}; + var s1 = new TestUnmanagedBlobStruct + { + Value = 4, + BlobAssetReference = CreateBlobArrayRef(array1) + }; + + float[] array2 = {2.7f, 3.6f}; + var s2 = new TestUnmanagedBlobStruct + { + Value = 6, + BlobAssetReference = CreateBlobArrayRef(array2) + }; + + Entity a = m_Manager.CreateEntity(); + m_Manager.AddSharedComponent(a, s1); + + Entity b = m_Manager.CreateEntity(); + m_Manager.AddSharedComponent(b, s2); + + var writer = new TestBinaryWriter(m_Manager.World.UpdateAllocator.ToAllocator); + SerializeUtility.SerializeWorld(m_Manager, writer); + + m_Manager.DestroyEntity(m_Manager.UniversalQuery); + + var deserializedWorld = new World("Deserialized World"); + var entityManager = deserializedWorld.EntityManager; + + using (var reader = new TestBinaryReader(writer)) + { + SerializeUtility.DeserializeWorld(entityManager.BeginExclusiveEntityTransaction(), reader); + entityManager.EndExclusiveEntityTransaction(); + } + + entityManager.Debug.CheckInternalConsistency(); + + entityManager.GetAllUniqueSharedComponents(out var sharedComponents, Allocator.Temp); + // Index 0 is reserved for the default value for the shared component + Assert.AreEqual(3, sharedComponents.Length, "Serialization / Deserialization failed - unexpected number of shared components"); + + var readS1 = sharedComponents[1]; + Assert.AreEqual(4, readS1.Value, "Expected the same int value for s1"); + var readArray1 = readS1.BlobAssetReference.Value.ToArray(); + CollectionAssert.AreEqual(array1, readArray1); + + var readS2 = sharedComponents[2]; + Assert.AreEqual(6, readS2.Value, "Expected the same int value for s2"); + var readArray2 = readS2.BlobAssetReference.Value.ToArray(); + CollectionAssert.AreEqual(array2, readArray2); + } + + [Test] + public void SerializeEntities_WithUnmanagedSharedComponentsSameBlob_Works() + { + float[] array = {1.7f, 2.6f}; + var blobRef = CreateBlobArrayRef(array); + var s1 = new TestUnmanagedBlobStruct + { + Value = 4, + BlobAssetReference = blobRef + }; + + var s2 = new TestUnmanagedBlobStruct + { + Value = 6, + BlobAssetReference = blobRef + }; + + Entity a = m_Manager.CreateEntity(); + m_Manager.AddSharedComponent(a, s1); + + Entity b = m_Manager.CreateEntity(); + m_Manager.AddSharedComponent(b, s2); + + var writer = new TestBinaryWriter(m_Manager.World.UpdateAllocator.ToAllocator); + SerializeUtility.SerializeWorld(m_Manager, writer); + + m_Manager.DestroyEntity(m_Manager.UniversalQuery); + + var deserializedWorld = new World("Deserialized World"); + var entityManager = deserializedWorld.EntityManager; + + using (var reader = new TestBinaryReader(writer)) + { + SerializeUtility.DeserializeWorld(entityManager.BeginExclusiveEntityTransaction(), reader); + entityManager.EndExclusiveEntityTransaction(); + } + + entityManager.Debug.CheckInternalConsistency(); + + entityManager.GetAllUniqueSharedComponents(out var sharedComponents, Allocator.Temp); + // Index 0 is reserved for the default value for the shared component + Assert.AreEqual(3, sharedComponents.Length, "Serialization / Deserialization failed - unexpected number of shared components"); + + var readS1 = sharedComponents[1]; + Assert.AreEqual(4, readS1.Value, "Expected the same int value for s1"); + var readArray1 = readS1.BlobAssetReference.Value.ToArray(); + CollectionAssert.AreEqual(array, readArray1); + + var readS2 = sharedComponents[2]; + Assert.AreEqual(6, readS2.Value, "Expected the same int value for s2"); + var readArray2 = readS2.BlobAssetReference.Value.ToArray(); + CollectionAssert.AreEqual(array, readArray2); + + // Make sure the blob is shared + unsafe + { + Assert.IsTrue((byte*)readS1.BlobAssetReference.GetUnsafePtr() == (byte*)readS2.BlobAssetReference.GetUnsafePtr(), "Both components should point to the same blob"); + } + } + struct TestManagedStruct : ISharedComponentData, IEquatable { public int Value; @@ -2806,7 +2853,6 @@ public void SerializeEntities_WithEntityReferencesInUnmanagedSharedComponents_Re } #else [Test] - [DotsRuntimeIncompatibleTest("We cannot perform the validation checks in DOTS Runtime currently")] public void SerializeEntities_WithEntityReferencesInUnmanagedSharedComponents_ThrowsArgumentException() { { @@ -2876,7 +2922,7 @@ public struct SerializableSharedComponentWithEntityReference : ISharedComponentD [Test] public void SerializeEntities_SharedComponentWithEntityThrows() { - var blobArchetype = m_Manager.CreateArchetype(typeof(SharedComponentWithEntityReference)); + var blobArchetype = m_Manager.CreateArchetype(typeof(SharedComponentWithEntityReference)); m_Manager.CreateEntity(blobArchetype); var writer = new TestBinaryWriter(m_Manager.World.UpdateAllocator.ToAllocator); diff --git a/Unity.Entities.Tests/SharedComponentDataTests.cs b/Unity.Entities.Tests/SharedComponentDataTests.cs index de255d9..1ff6270 100644 --- a/Unity.Entities.Tests/SharedComponentDataTests.cs +++ b/Unity.Entities.Tests/SharedComponentDataTests.cs @@ -169,8 +169,6 @@ public override int GetHashCode() } } -#if !NET_DOTS - struct ManagedSharedData1 : ISharedComponentData, IEquatable { public Tuple value; @@ -200,7 +198,6 @@ public override int GetHashCode() return (value != null ? value.GetHashCode() : 0); } } -#endif struct ManagedSharedData2 : ISharedComponentData, IEquatable { public int value; @@ -312,7 +309,6 @@ public void UnmanagedSharedComponent() Assert.IsFalse(m_Manager.HasComponent(ue1)); // Managed path -#if !NET_DOTS m_Manager.AddSharedComponentManaged(me1, new ManagedSharedData1(new Tuple(17, 3))); m_Manager.AddSharedComponentManaged(me2, new ManagedSharedData1(new Tuple(17, 3))); @@ -322,7 +318,6 @@ public void UnmanagedSharedComponent() m_Manager.RemoveComponent(me1); m_Manager.RemoveComponent(me2); -#endif // Unmanaged path m_Manager.AddSharedComponentManaged(ue1, new SharedData1()); @@ -379,10 +374,9 @@ public void GetSharedComponentCount() m_Manager.AddSharedComponentManaged(ue1, new SharedData2(18)); Assert.AreEqual(startCount + 2, m_Manager.GetSharedComponentCount()); -#if !NET_DOTS m_Manager.AddSharedComponentManaged(ue1, new ManagedSharedData1(new Tuple(2, 3))); Assert.AreEqual(startCount + 3, m_Manager.GetSharedComponentCount()); -#endif + // ###REVIEW NOTE### Managed Path doesn't clear the SharedDataComponent when they're no longer referenced, should we fix this behavior or keep it? // m_Manager.RemoveComponent(ue1); // Assert.AreEqual(startCount + 2, m_Manager.GetSharedComponentCount()); @@ -1095,8 +1089,6 @@ public void GetSharedComponentDataWithTypeIndex() Assert.AreEqual(17, ((SharedData1)sharedComponentValue).value); } -#if !NET_DOTS //custom equality / iequatable shared components not supported in dotsrt yet - [Test] public void Case1085730() { @@ -1130,7 +1122,6 @@ public void Case1085730_Equals() Assert.IsTrue(iseq); } -#endif public struct CustomEquality : ISharedComponentData, IEquatable { diff --git a/Unity.Entities.Tests/SingletonTests.cs b/Unity.Entities.Tests/SingletonTests.cs index f6fd0e5..c54b2bb 100644 --- a/Unity.Entities.Tests/SingletonTests.cs +++ b/Unity.Entities.Tests/SingletonTests.cs @@ -201,6 +201,22 @@ public void HasSingleton_EnableableComponent_Throws() Assert.Throws(() => query.HasSingleton()); } + [Test] + [TestRequiresDotsDebugOrCollectionChecks("Test requires opt-in debug checks")] + public void HasSingleton_MoreThanOneInstance_Throws() + { + var e1 = m_Manager.CreateEntity(typeof(EcsIntElement), typeof(EcsTestSharedComp)); + var e2 = m_Manager.CreateEntity(typeof(EcsIntElement), typeof(EcsTestSharedComp)); + var query = new EntityQueryBuilder(EmptySystem.WorldUpdateAllocator).WithAllRW().Build(EmptySystem); + Assert.Throws(() => query.HasSingleton()); + // If the query has filtering enabled and only one instance matches the filter, this should not throw + var sharedComponentValue = new EcsTestSharedComp { value = 17 }; + m_Manager.SetSharedComponent(e2, sharedComponentValue); + query.SetSharedComponentFilter(sharedComponentValue); + Assert.DoesNotThrow(() => query.HasSingleton()); + Assert.AreEqual(e2, query.GetSingletonEntity()); + } + [Test] public void GetSingletonBuffer_Works() { @@ -443,9 +459,6 @@ public void HasSingleton_ReturnsTrueWithEntityWithOnlyComponent() m_Manager.CreateEntity(typeof(EcsTestData)); Assert.IsTrue(query.HasSingleton()); - - m_Manager.CreateEntity(typeof(EcsTestData)); - Assert.IsFalse(query.HasSingleton()); } [Test] diff --git a/Unity.Entities.Tests/SizeTests.cs b/Unity.Entities.Tests/SizeTests.cs index 1fe6e83..47a1860 100644 --- a/Unity.Entities.Tests/SizeTests.cs +++ b/Unity.Entities.Tests/SizeTests.cs @@ -20,10 +20,10 @@ public void SIZ_TagComponentDoesNotChangeCapacity() // a system ran, the version should match the global var chunk0 = access->EntityComponentStore->GetChunk(entity0); var chunk1 = access->EntityComponentStore->GetChunk(entity1); - var archetype0 = chunk0->Archetype; - var archetype1 = chunk1->Archetype; + var archetype0 = access->EntityComponentStore->GetArchetype(chunk0); + var archetype1 = access->EntityComponentStore->GetArchetype(chunk1); - ChunkDataUtility.GetIndexInTypeArray(chunk0->Archetype, TypeManager.GetTypeIndex()); + ChunkDataUtility.GetIndexInTypeArray(archetype0, TypeManager.GetTypeIndex()); Assert.True(ChunkDataUtility.AreLayoutCompatible(archetype0, archetype1)); } @@ -39,8 +39,8 @@ public void SIZ_TagComponentZeroSize() var ecs = access->EntityComponentStore; // a system ran, the version should match the global var chunk0 = ecs->GetChunk(entity0); - var archetype0 = chunk0->Archetype; - var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(chunk0->Archetype, TypeManager.GetTypeIndex()); + var archetype0 = ecs->GetArchetype(chunk0); + var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype0, TypeManager.GetTypeIndex()); Assert.AreEqual(0, archetype0->SizeOfs[indexInTypeArray]); } diff --git a/Unity.Entities.Tests/StateAllocatorTests.cs b/Unity.Entities.Tests/StateAllocatorTests.cs index 636ffe6..de87c2b 100644 --- a/Unity.Entities.Tests/StateAllocatorTests.cs +++ b/Unity.Entities.Tests/StateAllocatorTests.cs @@ -103,8 +103,6 @@ public void SimpleTest() Assert.AreNotEqual(v2, v2_); } - -#if !NET_DOTS [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] private static void ThrowCountIsWrong() { @@ -192,7 +190,5 @@ public void StressTestFromMono() RunStressTest((IntPtr)p, (IntPtr)s); } } - -#endif } } diff --git a/Unity.Entities.Tests/SystemAPI/TestEntitiesForEach.cs b/Unity.Entities.Tests/SystemAPI/TestEntitiesForEach.cs index f6f3d97..d46adef 100644 --- a/Unity.Entities.Tests/SystemAPI/TestEntitiesForEach.cs +++ b/Unity.Entities.Tests/SystemAPI/TestEntitiesForEach.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using NUnit.Framework; using Unity.Core; using Unity.Mathematics; @@ -13,7 +12,7 @@ public partial class TestEntitiesForEach : ECSTestsFixture public void SetUp() { World.CreateSystem(); - World.CreateSystem(); + World.CreateSystem(); World.CreateSystem(); } @@ -24,7 +23,7 @@ public void SetUp() #region Aspect [Test] - public void GetAspect() => World.GetExistingSystem().Update(World.Unmanaged); + public void GetAspectRW() => World.GetExistingSystem().Update(World.Unmanaged); #endregion @@ -56,7 +55,7 @@ public void Move(float3 newPosition) } } - partial class TestGetAspect : SystemBase { + partial class TestGetAspectRW : SystemBase { protected override void OnCreate() {} protected override void OnDestroy() {} protected override void OnUpdate() { @@ -90,4 +89,3 @@ protected override void OnUpdate() } #endregion } -#endif diff --git a/Unity.Entities.Tests/SystemAPI/TestISystemManaged.cs b/Unity.Entities.Tests/SystemAPI/TestISystemManaged.cs index 060c7a5..bc409a4 100644 --- a/Unity.Entities.Tests/SystemAPI/TestISystemManaged.cs +++ b/Unity.Entities.Tests/SystemAPI/TestISystemManaged.cs @@ -1,4 +1,4 @@ -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME // UnityEngine.Objects are not allowed in DOTS Runtime. +#if !UNITY_DISABLE_MANAGED_COMPONENTS using System; using NUnit.Framework; diff --git a/Unity.Entities.Tests/SystemAPIQueryBuilderCodeGen/SystemAPIQueryBuilderCodeGenTests.cs b/Unity.Entities.Tests/SystemAPIQueryBuilderCodeGen/SystemAPIQueryBuilderCodeGenTests.cs index 2a63230..603ac8d 100644 --- a/Unity.Entities.Tests/SystemAPIQueryBuilderCodeGen/SystemAPIQueryBuilderCodeGenTests.cs +++ b/Unity.Entities.Tests/SystemAPIQueryBuilderCodeGen/SystemAPIQueryBuilderCodeGenTests.cs @@ -50,6 +50,7 @@ public unsafe void WithAllTheThings() .WithNone() .WithDisabled() .WithAbsent() + .WithPresent() .WithOptions(EntityQueryOptions.IncludeDisabledEntities) .Build(); var queryData = query._GetImpl()->_QueryData; @@ -63,12 +64,14 @@ public unsafe void WithAllTheThings() Assert.AreEqual(1, archetypeQuery.AnyCount); Assert.AreEqual(1, archetypeQuery.DisabledCount); Assert.AreEqual(1, archetypeQuery.AbsentCount); + Assert.AreEqual(1, archetypeQuery.PresentCount); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.All[0]); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.Any[0]); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.None[0]); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.Disabled[0]); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.Absent[0]); + Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery.Present[0]); Assert.AreEqual(EntityQueryOptions.IncludeDisabledEntities, archetypeQuery.Options); } @@ -92,6 +95,7 @@ public unsafe void CreateArchetypeQueries_WithRO_AndRW() Assert.AreEqual(0, archetypeQuery1.AnyCount); Assert.AreEqual(0, archetypeQuery1.DisabledCount); Assert.AreEqual(0, archetypeQuery1.AbsentCount); + Assert.AreEqual(0, archetypeQuery1.PresentCount); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery1.All[0]); Assert.AreEqual(ComponentType.ReadOnly().TypeIndex, archetypeQuery1.None[0]); @@ -103,6 +107,7 @@ public unsafe void CreateArchetypeQueries_WithRO_AndRW() Assert.AreEqual(2, archetypeQuery2.AnyCount); Assert.AreEqual(0, archetypeQuery2.DisabledCount); Assert.AreEqual(0, archetypeQuery2.AbsentCount); + Assert.AreEqual(0, archetypeQuery2.PresentCount); Assert.AreEqual(ComponentType.ReadWrite().TypeIndex, archetypeQuery2.All[0]); Assert.That(ToManagedArray(archetypeQuery2.Any, archetypeQuery2.AnyCount), Is.EquivalentTo(new TypeIndex[] { @@ -160,6 +165,7 @@ public unsafe void WithAspect() Assert.AreEqual(0, archetypeQuery.AnyCount); Assert.AreEqual(0, archetypeQuery.DisabledCount); Assert.AreEqual(0, archetypeQuery.AbsentCount); + Assert.AreEqual(0, archetypeQuery.PresentCount); Assert.AreEqual(ComponentType.ReadWrite(), archetypeQuery.GetComponentTypeAllAt(0)); } @@ -191,6 +197,7 @@ public unsafe void WithAspect2() Assert.AreEqual(0, archetypeQuery.AnyCount); Assert.AreEqual(0, archetypeQuery.DisabledCount); Assert.AreEqual(0, archetypeQuery.AbsentCount); + Assert.AreEqual(0, archetypeQuery.PresentCount); } public unsafe void WithAspectAliased() @@ -208,6 +215,7 @@ public unsafe void WithAspectAliased() Assert.AreEqual(0, archetypeQuery.AnyCount); Assert.AreEqual(0, archetypeQuery.DisabledCount); Assert.AreEqual(0, archetypeQuery.AbsentCount); + Assert.AreEqual(0, archetypeQuery.PresentCount); Assert.AreEqual(ComponentType.ReadWrite(), archetypeQuery.GetComponentTypeAllAt(0)); } diff --git a/Unity.Entities.Tests/SystemBaseDependencyTests.cs b/Unity.Entities.Tests/SystemBaseDependencyTests.cs index 05dd7eb..fab08fd 100644 --- a/Unity.Entities.Tests/SystemBaseDependencyTests.cs +++ b/Unity.Entities.Tests/SystemBaseDependencyTests.cs @@ -89,7 +89,6 @@ protected override void OnUpdate() } } -#if !NET_DOTS [Test] public void CreatingGenericSystem_Works() { @@ -98,10 +97,8 @@ public void CreatingGenericSystem_Works() system.Update(); Assert.AreEqual(system.thing, system.thing2); } -#endif [Test] - [DotsRuntimeFixme("Debug.LogError is not burst compatible (for safety errors reported from bursted code) and LogAssert.Expect is not properly implemented in DOTS Runtime - DOTS-4294")] [TestRequiresCollectionChecks("Requires Job Safety System")] public void ReturningWrongJobReportsCorrectSystemUpdate() { @@ -705,10 +702,7 @@ public void BufferDependencies_GetBufferReadInUpdateDependsOnGetBufferWriteJob() var readHandle = sysRead.CheckedState()->Dependency; Assert.IsFalse(writeHandle.Equals(readHandle)); #if ENABLE_UNITY_COLLECTIONS_CHECKS -// Investigate why this fails in DOTS Runtime DOTS-5964 -#if !UNITY_DOTSRUNTIME Assert.IsTrue(JobHandle.CheckFenceIsDependencyOrDidSyncFence(readHandle, writeHandle)); -#endif #endif } } @@ -729,9 +723,7 @@ public void BufferDependencies_GetBufferReadInUpdateDependsOnGetBufferLookupWrit var readHandle = sysRead.CheckedState()->Dependency; Assert.IsFalse(writeHandle.Equals(readHandle)); #if ENABLE_UNITY_COLLECTIONS_CHECKS -#if !UNITY_DOTSRUNTIME Assert.IsTrue(JobHandle.CheckFenceIsDependencyOrDidSyncFence(readHandle, writeHandle)); -#endif #endif } } @@ -752,9 +744,7 @@ public void BufferDependencies_GetBufferReadInUpdateDependsOnGetBufferLookupForE var readHandle = sysRead.CheckedState()->Dependency; Assert.IsFalse(writeHandle.Equals(readHandle)); #if ENABLE_UNITY_COLLECTIONS_CHECKS -#if !UNITY_DOTSRUNTIME Assert.IsTrue(JobHandle.CheckFenceIsDependencyOrDidSyncFence(readHandle, writeHandle)); -#endif #endif } } @@ -988,14 +978,10 @@ public void ParallelWrite_Exception_Not_Suppressed() // The data access is safe but the safety system can't know that. We had an issue where the safety PanicFunction // was preventing the real safety message from surfacing var sys1 = World.CreateSystem(); -#if UNITY_DOTSRUNTIME - Assert.Throws(() => { sys1.Update(World.Unmanaged); }); -#else Assert.That(() => { sys1.Update(World.Unmanaged); }, Throws.Exception.TypeOf() .With.Message.Contains( "The previously scheduled job SystemSchedulingTwoParallelJobsWritingToSameArray:Discriminator1Job writes to the Unity.Collections.NativeArray")); -#endif LogAssert.Expect(LogType.Error, "The system Unity.Entities.Tests.SystemBaseDependencyTests+SystemSchedulingTwoParallelJobsWritingToSameArray reads Unity.Entities.Tests.EcsTestData via SystemSchedulingTwoParallelJobsWritingToSameArray:Discriminator1Job but that type was not assigned to the Dependency property. To ensure correct behavior of other systems, the job or a dependency must be assigned to the Dependency property before returning from the OnUpdate method."); } } diff --git a/Unity.Entities.Tests/SystemBaseSingletonAccessTests.cs b/Unity.Entities.Tests/SystemBaseSingletonAccessTests.cs index e43e62d..827df24 100644 --- a/Unity.Entities.Tests/SystemBaseSingletonAccessTests.cs +++ b/Unity.Entities.Tests/SystemBaseSingletonAccessTests.cs @@ -241,9 +241,13 @@ public void HasSingleton_ReturnsTrueWithEntityWithOnlyComponent() EntityManager.CreateEntity(typeof(EcsTestData)); Assert.IsTrue(SystemAPI.HasSingleton()); + } + public void HasSingleton_MoreThanOneInstance_Throws() + { EntityManager.CreateEntity(typeof(EcsTestData)); - Assert.IsFalse(SystemAPI.HasSingleton()); + EntityManager.CreateEntity(typeof(EcsTestData)); + Assert.Throws(() => SystemAPI.HasSingleton()); } public void GetSingletonEntityWorks() @@ -355,6 +359,10 @@ public void HasSingletonWorks_ManagedComponents() [Test] public void SystemBase_HasSingleton_ReturnsTrueWithEntityWithOnlyComponent() => TestSystem.HasSingleton_ReturnsTrueWithEntityWithOnlyComponent(); + [Test] + [TestRequiresDotsDebugOrCollectionChecks("requires opt-in debug checks")] + public void SystemBase_HasSingleton_MoreThanOneInstance_Throws() => TestSystem.HasSingleton_MoreThanOneInstance_Throws(); + [Test] public void SystemBase_GetSingletonEntityWorks() => TestSystem.GetSingletonEntityWorks(); diff --git a/Unity.Entities.Tests/SystemSafetyTests_DependenciesAndJobDebugger.cs b/Unity.Entities.Tests/SystemSafetyTests_DependenciesAndJobDebugger.cs index 9306777..255b191 100644 --- a/Unity.Entities.Tests/SystemSafetyTests_DependenciesAndJobDebugger.cs +++ b/Unity.Entities.Tests/SystemSafetyTests_DependenciesAndJobDebugger.cs @@ -115,7 +115,6 @@ public void OnUpdate(ref SystemState state) #endregion [Test] - [DotsRuntimeFixme("Debug.LogError is not burst compatible (for safety errors reported from bursted code) and LogAssert.Expect is not properly implemented in DOTS Runtime - DOTS-4294")] [TestRequiresCollectionChecks("Requires Job Safety System")] public void MissedDependencyMakesActionableErrorMessage([Values]bool iSystem) { @@ -148,7 +147,6 @@ public void MissedDependencyMakesActionableErrorMessage([Values]bool iSystem) } [Test] - [DotsRuntimeFixme("Debug.LogError is not burst compatible (for safety errors reported from bursted code) and LogAssert.Expect is not properly implemented in DOTS Runtime - DOTS-4294")] [TestRequiresCollectionChecks("Requires Job Safety System")] public void MissedDependencyFromNestedUpdateMakesActionableErrorMessage([Values]bool iSystem) { @@ -229,10 +227,8 @@ public void OnUpdate(ref SystemState state) } } -#if !NET_DOTS [Ignore("DOTS-6905 Needs re-evaluated after we solve the NullReferenceException issues")] [Test] - [DotsRuntimeFixme("Debug.LogError is not burst compatible (for safety errors reported from bursted code) and LogAssert.Expect is not properly implemented in DOTS Runtime - DOTS-4294")] public void NoExtraMessageFromForEachSystemRepro([Values]bool iSystem) { var arch = World.EntityManager.CreateArchetype(typeof(EcsTestData), typeof(EcsTestData2)); @@ -251,7 +247,5 @@ public void NoExtraMessageFromForEachSystemRepro([Values]bool iSystem) LogAssert.Expect(LogType.Exception, regex); } -#endif - } } diff --git a/Unity.Entities.Tests/SystemSafetyTests_SystemStateConsistency.cs b/Unity.Entities.Tests/SystemSafetyTests_SystemStateConsistency.cs index ca7ddbf..b6efec9 100644 --- a/Unity.Entities.Tests/SystemSafetyTests_SystemStateConsistency.cs +++ b/Unity.Entities.Tests/SystemSafetyTests_SystemStateConsistency.cs @@ -95,7 +95,7 @@ unsafe protected override void OnUpdate() } [Test] -#if !UNITY_DOTSRUNTIME && !UNITY_WEBGL +#if !UNITY_WEBGL [ConditionalIgnore("IgnoreForCoverage", "Fails randonly when ran with code coverage enabled")] #endif public void ForEachProtectionDoesntLeakWhenThrowingISystemBase() @@ -168,7 +168,7 @@ unsafe protected override void OnUpdate() } [Test] -#if !UNITY_DOTSRUNTIME && !UNITY_WEBGL +#if !UNITY_WEBGL [ConditionalIgnore("IgnoreForCoverage", "Fails randonly when ran with code coverage enabled")] #endif public void CallUpdateFromUpdateWorks() @@ -329,7 +329,7 @@ unsafe override protected void OnUpdate() } [Test] -#if !UNITY_DOTSRUNTIME && !UNITY_WEBGL +#if !UNITY_WEBGL [ConditionalIgnore("IgnoreForCoverage", "Fails randonly when ran with code coverage enabled")] #endif public void CreateAndUpdateNewWorldInSystemTest() diff --git a/Unity.Entities.Tests/TestCaseGeneric.cs b/Unity.Entities.Tests/TestCaseGeneric.cs index 46a30c5..9df32b2 100644 --- a/Unity.Entities.Tests/TestCaseGeneric.cs +++ b/Unity.Entities.Tests/TestCaseGeneric.cs @@ -1,5 +1,3 @@ -#if !UNITY_PORTABLE_TEST_RUNNER -// https://unity3d.atlassian.net/browse/DOTSR-1432 using System; using System.Collections.Generic; using NUnit.Framework; @@ -37,4 +35,3 @@ IEnumerable ITestBuilder.BuildFrom(IMethodInfo method, Test suite) } } } -#endif diff --git a/Unity.Entities.Tests/TestComponents.cs b/Unity.Entities.Tests/TestComponents.cs index dfbf4d7..155abb6 100644 --- a/Unity.Entities.Tests/TestComponents.cs +++ b/Unity.Entities.Tests/TestComponents.cs @@ -804,9 +804,6 @@ public EcsTestManagedDataEntity(string inValue0, Entity inValue1, int inValue2 = } } -#if !NET_DOTS -// https://unity3d.atlassian.net/browse/DOTSR-1432 - internal class EcsTestManagedDataEntityCollection : IComponentData { public List value0; @@ -824,7 +821,6 @@ public EcsTestManagedDataEntityCollection(string[] inValue0, Entity[] inValue1) nullField = null; } } -#endif internal class EcsTestManagedComponent : IComponentData { diff --git a/Unity.Entities.Tests/TypeManagerTests.cs b/Unity.Entities.Tests/TypeManagerTests.cs index e81df30..9f3b970 100644 --- a/Unity.Entities.Tests/TypeManagerTests.cs +++ b/Unity.Entities.Tests/TypeManagerTests.cs @@ -741,7 +741,6 @@ public void TypeOverrideWorks_Unmanaged_ValidTypesDoNotThrow() Assert.IsFalse(typeOverridesNoBlobNoEntityInfo.HasBlobAssetRefs); } -#if !UNITY_DOTSRUNTIME // No reflection support in TypeManager in DOTS Runtime even without TinyBCL; no UnityEngine in DOTS Runtime [DisableAutoTypeRegistration] struct NonBlittableComponentData : IComponentData { @@ -1085,7 +1084,6 @@ public void TestNestedNativeContainersDoesNotThrow() Assert.IsTrue(TypeManager.BuildComponentType(typeof(NestedNativeContainerComponent), new TypeManager.BuildComponentCache()).TypeIndex.HasNativeContainer); }); } -#endif #if !UNITY_DISABLE_MANAGED_COMPONENTS interface IBaseInterface @@ -1252,8 +1250,7 @@ public void TestHasBlobReferencesManaged() #endif - // Tests including Unityengine, or reflection -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS #pragma warning disable 649 class TestScriptableObjectWithFields : UnityEngine.ScriptableObject diff --git a/Unity.Entities.Tests/Types/ComponentTypeTests.cs b/Unity.Entities.Tests/Types/ComponentTypeTests.cs index 9f0fd59..92369cd 100644 --- a/Unity.Entities.Tests/Types/ComponentTypeTests.cs +++ b/Unity.Entities.Tests/Types/ComponentTypeTests.cs @@ -121,15 +121,10 @@ public unsafe void ComponentTypeSet_TooManyTypes_Throws() [TestRequiresDotsDebugOrCollectionChecks("Test requires component safety checks")] public void ComponentTypeSet_DuplicateTypes_Throws() { -#if UNITY_DOTSRUNTIME - Assert.Throws(() => new ComponentTypeSet(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData))); - Assert.Throws(() => new ComponentTypeSet(typeof(EcsTestData2), typeof(EcsTestData), typeof(EcsTestData))); -#else - Assert.That(() => new ComponentTypeSet(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData)), Throws.ArgumentException - .With.Message.Contains($"ComponentTypes cannot contain duplicate types. Remove all but one occurrence of \"Unity.Entities.Tests.EcsTestData\"")); - Assert.That(() => new ComponentTypeSet(typeof(EcsTestData2), typeof(EcsTestData), typeof(EcsTestData)), Throws.ArgumentException - .With.Message.Contains($"ComponentTypes cannot contain duplicate types. Remove all but one occurrence of \"Unity.Entities.Tests.EcsTestData\"")); -#endif + Assert.That(() => new ComponentTypeSet(typeof(EcsTestData), typeof(EcsTestData2), typeof(EcsTestData)), Throws.ArgumentException + .With.Message.Contains($"ComponentTypes cannot contain duplicate types. Remove all but one occurrence of \"Unity.Entities.Tests.EcsTestData\"")); + Assert.That(() => new ComponentTypeSet(typeof(EcsTestData2), typeof(EcsTestData), typeof(EcsTestData)), Throws.ArgumentException + .With.Message.Contains($"ComponentTypes cannot contain duplicate types. Remove all but one occurrence of \"Unity.Entities.Tests.EcsTestData\"")); } [Test] diff --git a/Unity.Entities.Tests/Types/Hash128Tests.cs b/Unity.Entities.Tests/Types/Hash128Tests.cs index 90b056e..e1fbbc6 100644 --- a/Unity.Entities.Tests/Types/Hash128Tests.cs +++ b/Unity.Entities.Tests/Types/Hash128Tests.cs @@ -1,8 +1,6 @@ using NUnit.Framework; -#if !NET_DOTS using System.Linq; using Random = System.Random; -#endif namespace Unity.Entities.Tests.Types { @@ -37,9 +35,6 @@ public void ToString_Basics() Is.EqualTo("feebdaed0000000000000000d00fdaab")); // vegan hash } -#if !NET_DOTS // Uses Linq and System.Random -// https://unity3d.atlassian.net/browse/DOTSR-1432 - [Test] public void ToString_ReturnsSameAsFramework() { @@ -61,8 +56,6 @@ string FrameworkToString(in Hash128 hash) } } -#endif - [Test] public void Comparisons() { @@ -123,7 +116,6 @@ public void Hash128_CanConvert_UnityEngine_Hash128_Strings() } #endif // UNITY_EDITOR - #if !NET_DOTS [Test] public void Conversions_MatchUnityEngine() { @@ -137,8 +129,6 @@ public void Conversions_MatchUnityEngine() Assert.That(hashUnity, Is.EqualTo(hashEntitiesToUnity)); } - #endif - [Test] public void StringToHash() { diff --git a/Unity.Entities.Tests/WorldSystemTests.cs b/Unity.Entities.Tests/WorldSystemTests.cs index 2c4b27a..8b0d418 100644 --- a/Unity.Entities.Tests/WorldSystemTests.cs +++ b/Unity.Entities.Tests/WorldSystemTests.cs @@ -332,8 +332,7 @@ public void OnCreate(ref SystemState state) } [Test] - [DotsRuntimeFixmeAttribute ("Assert.Throws becomes Assert.Throws, probably because of a inconstency in how burst exception handling forwarding works")] -#if !UNITY_DOTSRUNTIME && !UNITY_WEBGL +#if !UNITY_WEBGL [ConditionalIgnore("IgnoreForCoverage", "Fails randonly when ran with code coverage enabled")] #endif [TestRequiresDotsDebugOrCollectionChecks("Test requires system safety checks")] @@ -560,7 +559,6 @@ public void OnDestroy(ref SystemState systemState) } [Test] - [DotsRuntimeFixmeAttribute] public void ThrowDuringDestroyStillRemovesISystem() { using (var world = new World("WorldX")) @@ -571,7 +569,7 @@ public void ThrowDuringDestroyStillRemovesISystem() CheckUnmanagedSystemEmpty(world); } } - + public void TestCreateISystemAndLogExceptionsFailureIsolation(Type badSystem) { using (var world = new World("WorldX")) @@ -579,7 +577,7 @@ public void TestCreateISystemAndLogExceptionsFailureIsolation(Type badSystem) var unmanagedTypes = new NativeList(2, Allocator.Temp); unmanagedTypes.Add(TypeManager.GetSystemTypeIndex(badSystem)); unmanagedTypes.Add(TypeManager.GetSystemTypeIndex()); - + var unmanagedSystemHandles = world.GetOrCreateSystemsAndLogException(unmanagedTypes, unmanagedTypes.Length, Allocator.Temp); diff --git a/Unity.Entities.Tests/WorldTests.cs b/Unity.Entities.Tests/WorldTests.cs index ae1dc8a..14eeb15 100644 --- a/Unity.Entities.Tests/WorldTests.cs +++ b/Unity.Entities.Tests/WorldTests.cs @@ -6,11 +6,8 @@ using Unity.Collections.LowLevel.Unsafe; using Unity.Core; using Unity.Jobs; -#if !UNITY_PORTABLE_TEST_RUNNER using System.Reflection; using System.Linq; -#endif - namespace Unity.Entities.Tests { @@ -88,7 +85,7 @@ public unsafe void WorldNoOverlappingChunkSequenceNumbers() for (int j = 0; j < worldBChunks.Length; j++) { var chunkB = worldBChunks[i].m_Chunk; - var sequenceNumberDiff = chunkA->SequenceNumber - chunkB->SequenceNumber; + var sequenceNumberDiff = chunkA.SequenceNumber - chunkB.SequenceNumber; // Any chunk sequence numbers in different worlds should be separated by at least 32 bits Assert.IsTrue(sequenceNumberDiff > 1 << 32); @@ -109,7 +106,7 @@ public unsafe void WorldChunkSequenceNumbersNotReused() { var entity = worldA.EntityManager.CreateEntity(); var chunk = worldA.EntityManager.GetChunk(entity); - lastChunkSequenceNumber = chunk.m_Chunk->SequenceNumber; + lastChunkSequenceNumber = chunk.m_Chunk.SequenceNumber; worldA.EntityManager.DestroyEntity(entity); } @@ -118,7 +115,7 @@ public unsafe void WorldChunkSequenceNumbersNotReused() { var entity = worldA.EntityManager.CreateEntity(); var chunk = worldA.EntityManager.GetChunk(entity); - var chunkSequenceNumber = chunk.m_Chunk->SequenceNumber; + var chunkSequenceNumber = chunk.m_Chunk.SequenceNumber; // Sequence numbers should be increasing and should not be reused when chunk is re-used (after zero count) Assert.IsTrue(chunkSequenceNumber > lastChunkSequenceNumber); @@ -260,8 +257,6 @@ void AdvanceWorldTime(float amount) } } -#if !UNITY_PORTABLE_TEST_RUNNER -// https://unity3d.atlassian.net/browse/DOTSR-1432 [Test] public void DisposeAllWorlds() { @@ -286,10 +281,6 @@ public void DisposeAllWorlds() } } -#endif - -#if !UNITY_PORTABLE_TEST_RUNNER -// https://unity3d.atlassian.net/browse/DOTSR-1432 [Test] public void IteratingOverBoxedNoAllocReadOnlyCollectionThrows() { @@ -301,7 +292,6 @@ public void IteratingOverBoxedNoAllocReadOnlyCollectionThrows() Assert.That(ex.Message, Is.EqualTo($"To avoid boxing, do not cast {nameof(World.NoAllocReadOnlyCollection)} to IEnumerable.")); Assert.That(ex2.Message, Is.EqualTo($"To avoid boxing, do not cast {nameof(World.NoAllocReadOnlyCollection)} to IEnumerable.")); } -#endif #if !DOTS_DISABLE_DEBUG_NAMES [Test] @@ -436,7 +426,6 @@ public partial class MultiPhaseTestSystem3 : MultiPhaseTestSystem } [Test] - [IgnoreInPortableTests("There is an Assert.AreEqual(object, object) which in the OnStopRunning, which the runner doesn't find.")] public void World_Dispose_MultiPhaseSystemDestroy() { World world = new World("WorldX"); @@ -450,6 +439,65 @@ public void World_Dispose_MultiPhaseSystemDestroy() Assert.AreEqual(0, world.Systems.Count); } + [DisableAutoCreation] + public partial struct UnregisteredGenericSystem:ISystem + { + public void OnUpdate(ref SystemState state) + { + throw new InvalidOperationException("I ran!"); + } + } + [Test] + public void CreateSystem_WithUnregisteredSystemType_Throws() + { + // Creating unregistered unmanaged systems is not supported; the necessary reflection would not be Burst-compatible. + using var world = new World("Test World"); + SystemHandle sys = default; + Assert.Throws(() => { sys = world.CreateSystem>(); }); + } + [Test] + public void GetOrCreateSystem_WithUnregisteredSystemType_Throws() + { + // Creating unregistered unmanaged systems is not supported; the necessary reflection would not be Burst-compatible. + using var world = new World("Test World"); + SystemHandle sys = default; + Assert.Throws(() => { sys = world.GetOrCreateSystem>(); }); + } + + [DisableAutoCreation] + public partial class UnregisteredGenericSystemManaged:SystemBase + { + protected override void OnUpdate() + { + throw new InvalidOperationException("I ran!"); + } + } + [Test] + public void AddSystemManaged_WithUnregisteredSystemType_Works() + { + using var world = new World("Test World"); + var sys = new UnregisteredGenericSystemManaged(); + Assert.DoesNotThrow(() => world.AddSystemManaged(sys)); + Assert.That(() => sys.Update(), Throws.InvalidOperationException.With.Message.Contain("I ran!") ); + } + [Test] + public void CreateSystemManaged_WithUnregisteredSystemType_Works() + { + using var world = new World("Test World"); + UnregisteredGenericSystemManaged sys = default; + Assert.DoesNotThrow(() => { sys = world.CreateSystemManaged>(); }); + Assert.That(() => sys.Update(), Throws.InvalidOperationException.With.Message.Contain("I ran!") ); + } + [Test] + public void GetOrCreateSystemManaged_WithUnregisteredSystemType_Works() + { + using var world = new World("Test World"); + UnregisteredGenericSystemManaged sys = default; + Assert.DoesNotThrow(() => { sys = world.GetOrCreateSystemManaged>(); }); + Assert.That(() => sys.Update(), Throws.InvalidOperationException.With.Message.Contain("I ran!") ); + } + + #if !UNITY_DISABLE_MANAGED_COMPONENTS [Test] public void World_Dispose_DisposesManagedComponent() diff --git a/Unity.Entities.Tests/WorldUpdateAllocatorTests.cs b/Unity.Entities.Tests/WorldUpdateAllocatorTests.cs index bc9c203..cb7d549 100644 --- a/Unity.Entities.Tests/WorldUpdateAllocatorTests.cs +++ b/Unity.Entities.Tests/WorldUpdateAllocatorTests.cs @@ -5,7 +5,7 @@ namespace Unity.Entities.Tests { -#if !UNITY_DOTSRUNTIME && ENABLE_UNITY_COLLECTIONS_CHECKS +#if ENABLE_UNITY_COLLECTIONS_CHECKS public class WorldUpdateAllocatorTests : ECSTestsCommonBase { diff --git a/Unity.Entities.UI.Editor/AssemblyInfo.cs b/Unity.Entities.UI.Editor/AssemblyInfo.cs index 2ec4ebe..c7c75b7 100644 --- a/Unity.Entities.UI.Editor/AssemblyInfo.cs +++ b/Unity.Entities.UI.Editor/AssemblyInfo.cs @@ -6,8 +6,8 @@ [assembly: InternalsVisibleTo("Unity.Build.iOS")] [assembly: InternalsVisibleTo("Unity.Build.Android")] [assembly: InternalsVisibleTo("Unity.Entities.UI.Tests")] -[assembly: InternalsVisibleTo("Unity.Motion.Editor")] [assembly: InternalsVisibleTo("Unity.Entities.Runtime.Build")] [assembly: InternalsVisibleTo("Unity.Entities.Editor")] [assembly: InternalsVisibleTo("Unity.Entities.Editor.Properties")] [assembly: InternalsVisibleTo("Unity.Entities.Editor.Tests")] +[assembly: InternalsVisibleTo("Unity.Physics.Editor")] diff --git a/Unity.Entities/ApiCompatibilityLevelTest.cs b/Unity.Entities/ApiCompatibilityLevelTest.cs index 0369078..6ea3b70 100644 --- a/Unity.Entities/ApiCompatibilityLevelTest.cs +++ b/Unity.Entities/ApiCompatibilityLevelTest.cs @@ -1,3 +1,3 @@ -#if !NET_STANDARD_2_0 && !NET_DOTS +#if !NET_STANDARD_2_0 #warning Project is expected to have API Compatibility Level set to .NET Standard 2.0 #endif diff --git a/Unity.Entities/AspectTypes.cs b/Unity.Entities/AspectTypes.cs index a7c459d..744cdd0 100644 --- a/Unity.Entities/AspectTypes.cs +++ b/Unity.Entities/AspectTypes.cs @@ -42,6 +42,18 @@ public interface IAspectCreate : IQueryTypeParameter where T : IAspect /// /// The UnsafeList to add the "All" ComponentTypes requirements to void AddComponentRequirementsTo(ref UnsafeList all); + + /// + /// Complete all dependencies for this aspect before a read-only operation. + /// + /// SystemState for the system from which the Aspect originates. You should only use Aspects in their originating system. + void CompleteDependencyBeforeRO(ref SystemState state); + + /// + /// Complete all dependencies for this aspect before a read-write operation. + /// + /// SystemState for the system from which the Aspect originates. You should only use Aspects in their originating system. + void CompleteDependencyBeforeRW(ref SystemState state); } /// diff --git a/Unity.Entities/AssemblyInfo.cs b/Unity.Entities/AssemblyInfo.cs index 9beebd8..ec7f39c 100644 --- a/Unity.Entities/AssemblyInfo.cs +++ b/Unity.Entities/AssemblyInfo.cs @@ -1,7 +1,5 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Unity.Motion")] -[assembly: InternalsVisibleTo("Unity.Motion.Tests")] [assembly: InternalsVisibleTo("Unity.Authoring")] [assembly: InternalsVisibleTo("Unity.Burst.Tests")] [assembly: InternalsVisibleTo("Unity.DataFlowGraph")] @@ -12,6 +10,10 @@ [assembly: InternalsVisibleTo("Unity.Entities.Editor")] [assembly: InternalsVisibleTo("Unity.Entities.Editor.PerformanceTests")] [assembly: InternalsVisibleTo("Unity.Entities.Editor.Tests")] +[assembly: InternalsVisibleTo("Unity.Entities.Graphics")] +[assembly: InternalsVisibleTo("Unity.Entities.Graphics.CodeGen")] +[assembly: InternalsVisibleTo("Unity.Entities.Graphics.Tests")] +[assembly: InternalsVisibleTo("Unity.Entities.Graphics.Tests.CodeGen")] [assembly: InternalsVisibleTo("Unity.Entities.Hybrid")] [assembly: InternalsVisibleTo("Unity.Entities.Hybrid.CodeGen")] [assembly: InternalsVisibleTo("Unity.Entities.Hybrid.CodeGen.Tests")] diff --git a/Unity.Entities/BlobBuilder.cs b/Unity.Entities/BlobBuilder.cs index 7904eb8..c3ab086 100644 --- a/Unity.Entities/BlobBuilder.cs +++ b/Unity.Entities/BlobBuilder.cs @@ -101,7 +101,7 @@ public int Length /// /// /// - unsafe public struct BlobBuilder : IDisposable + unsafe public partial struct BlobBuilder : IDisposable { AllocatorManager.AllocatorHandle m_allocator; NativeList m_allocations; @@ -307,8 +307,12 @@ public int CompareTo(SortedIndex other) /// Returns a reference to the blob asset in unmanaged memory. public BlobAssetReference CreateBlobAssetReference(AllocatorManager.AllocatorHandle allocator) where T : unmanaged { - //Align last chunk upwards so all chunks are 16 byte aligned - AlignChunk(m_currentChunkIndex); + // Avoid crash when there are no chunks (DOTS-8681) + if (m_currentChunkIndex != -1) + { + //Align last chunk upwards so all chunks are 16 byte aligned + AlignChunk(m_currentChunkIndex); + } var offsets = new NativeArray(m_allocations.Length + 1, Allocator.Temp); var sortedAllocs = new NativeArray(m_allocations.Length, Allocator.Temp); @@ -473,6 +477,12 @@ private bool GetPatchTarget(void* address, out BlobDataRef blobDataRef) return false; } + /// + /// Returns true if this BlobBuilder has been allocated. + /// + public readonly bool IsCreated + => m_allocations.IsCreated; + /// /// Disposes of this BlobBuilder instance and frees its temporary memory allocations. /// diff --git a/Unity.Entities/Blobs.cs b/Unity.Entities/Blobs.cs index 27b19fe..7512885 100644 --- a/Unity.Entities/Blobs.cs +++ b/Unity.Entities/Blobs.cs @@ -216,12 +216,13 @@ internal unsafe struct BlobAssetReferenceData : IEquatable /// This member is exposed to Unity.Properties to support EqualityComparison and Serialization within managed objects. /// @@ -245,7 +246,6 @@ long SerializedHash get => m_Align8Union; set => m_Align8Union = value; } -#endif [BurstDiscard] void ValidateNonBurst() @@ -279,13 +279,19 @@ void ValidateBurst() [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] public void ValidateNotNull() { - if(m_Ptr == null) - throw new InvalidOperationException("The BlobAssetReference is null."); + ThrowIfNull(); ValidateNonBurst(); ValidateBurst(); } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] + void ThrowIfNull() + { + if (m_Ptr == null) + throw new NullReferenceException("The BlobAssetReference is null."); + } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] public void ValidateAllowNull() { @@ -371,6 +377,8 @@ bool IsProtected { get { + ThrowIfNull(); + #if UNITY_64 return Header->ValidationPtr == (void*)~(long)m_Ptr; #else @@ -386,16 +394,12 @@ bool IsProtected /// Create a blob asset using a or by deserializing a serialized blob asset. /// The struct data type defining the data structure of the blob asset. [ChunkSerializable] -#if !NET_DOTS [DebuggerTypeProxy(typeof(DebugProxies.BlobAssetReferenceProxy<>))] [DebuggerDisplay(nameof(BlobAssetReference))] -#endif public unsafe struct BlobAssetReference : IDisposable, IEquatable> where T : unmanaged { -#if !NET_DOTS [Properties.CreateProperty] -#endif internal BlobAssetReferenceData m_data; /// /// Reports whether this instance references a valid blob asset. @@ -543,7 +547,6 @@ public static bool TryRead(U binaryReader, int version, out BlobAssetReferenc return true; } -#if !UNITY_DOTSRUNTIME /// /// Reads bytes from a fileName, validates the expected serialized version, and deserializes them into a new blob asset. /// @@ -563,30 +566,6 @@ public static bool TryRead(string path, int version, out BlobAssetReference r return TryRead(binaryReader, version, out result); } } -#else - /// - /// Reads bytes from a buffer, validates the expected serialized version, and deserializes them into a new blob asset. - /// - /// A byte stream of the blob data to read. - /// Length in bytes of the data block/param> - /// Expected version number of the blob data. - /// The resulting BlobAssetReference if the data was read successful. - /// A bool if the read was successful or not. - public static bool TryRead(byte* data, long length, int version, out BlobAssetReference result) - { - var binaryReader = new MemoryBinaryReader(data, length); - var storedVersion = binaryReader.ReadInt(); - if (storedVersion != version) - { - result = default; - return false; - } - - result = binaryReader.Read(); - - return true; - } -#endif /// /// Reads bytes from a buffer, validates the expected serialized version, and deserializes them into a new blob asset. @@ -644,7 +623,7 @@ public static void Write(U writer, BlobBuilder builder, int version) writer.Write(asset); } } -#if !NET_DOTS + /// /// Writes the blob data to a path with serialized version. /// @@ -658,7 +637,6 @@ public static void Write(BlobBuilder builder, string path, int version) Write(writer, builder, version); } } -#endif /// /// A "null" blob asset reference that can be used to test if a BlobAssetReference instance diff --git a/Unity.Entities/BlobsDebug.cs b/Unity.Entities/BlobsDebug.cs index d73507b..90d3edf 100644 --- a/Unity.Entities/BlobsDebug.cs +++ b/Unity.Entities/BlobsDebug.cs @@ -1,4 +1,3 @@ -#if !NET_DOTS using System; using System.Diagnostics; using System.Reflection; @@ -232,4 +231,3 @@ internal static unsafe object UnpackValue(void* basePtr, Type type) } } } -#endif diff --git a/Unity.Entities/ChunkDataUtility.cs b/Unity.Entities/ChunkDataUtility.cs index e1b1d4a..90a6813 100644 --- a/Unity.Entities/ChunkDataUtility.cs +++ b/Unity.Entities/ChunkDataUtility.cs @@ -124,21 +124,21 @@ public static void SetSharedComponentDataIndex(Entity entity, Archetype* archety entityComponentStore->Move(entity, archetype, sharedComponentValues); var chunk = entityComponentStore->GetChunk(entity); - Assert.AreEqual((ulong)archetype, (ulong)chunk->Archetype); // chunk should still be in the same archetype + Assert.AreEqual((ulong)archetype, (ulong)entityComponentStore->GetArchetype(chunk)); // chunk should still be in the same archetype var indexInTypeArray = GetIndexInTypeArray(archetype, typeIndex); var globalSystemVersion = entityComponentStore->GlobalSystemVersion; - archetype->Chunks.SetChangeVersion(indexInTypeArray, chunk->ListIndex, globalSystemVersion); + archetype->Chunks.SetChangeVersion(indexInTypeArray, chunk.ListIndex, globalSystemVersion); } - public static void SetSharedComponentDataIndex(Chunk* chunk, Archetype* archetype, in SharedComponentValues sharedComponentValues, TypeIndex typeIndex) + public static void SetSharedComponentDataIndex(ChunkIndex chunk, Archetype* archetype, in SharedComponentValues sharedComponentValues, TypeIndex typeIndex) { var entityComponentStore = archetype->EntityComponentStore; entityComponentStore->Move(chunk, archetype, sharedComponentValues); - Assert.AreEqual((ulong)archetype, (ulong)chunk->Archetype); // chunk should still be in the same archetype + Assert.AreEqual((ulong)archetype, (ulong)entityComponentStore->GetArchetype(chunk)); // chunk should still be in the same archetype var indexInTypeArray = GetIndexInTypeArray(archetype, typeIndex); var globalSystemVersion = entityComponentStore->GlobalSystemVersion; - archetype->Chunks.SetChangeVersion(indexInTypeArray, chunk->ListIndex, globalSystemVersion); + archetype->Chunks.SetChangeVersion(indexInTypeArray, chunk.ListIndex, globalSystemVersion); } public static void SetSharedComponentDataIndex(EntityBatchInChunk batch, Archetype* archetype, in SharedComponentValues sharedComponentValues, TypeIndex typeIndex) @@ -149,39 +149,48 @@ public static void SetSharedComponentDataIndex(EntityBatchInChunk batch, Archety // This variant returns an invalid pointer if the component is not present. // If you'd like a null pointer in this case instead, use GetOptionalComponentDataWithTypeRO() - public static byte* GetComponentDataWithTypeRO(Chunk* chunk, Archetype* archetype, int baseEntityIndex, TypeIndex typeIndex, ref LookupCache lookupCache) + public static byte* GetComponentDataWithTypeRO(ChunkIndex chunk, Archetype* archetype, int baseEntityIndex, TypeIndex typeIndex, ref LookupCache lookupCache) { if (Hint.Unlikely(lookupCache.Archetype != archetype)) lookupCache.Update(archetype, typeIndex); - return chunk->Buffer + (lookupCache.ComponentOffset + lookupCache.ComponentSizeOf * baseEntityIndex); + return chunk.Buffer + (lookupCache.ComponentOffset + lookupCache.ComponentSizeOf * baseEntityIndex); } // This variant returns an invalid pointer if the component is not present. // If you'd like a null pointer in this case instead, use GetOptionalComponentDataWithTypeRW() - public static byte* GetComponentDataWithTypeRW(Chunk* chunk, Archetype* archetype, int baseEntityIndex, TypeIndex typeIndex, uint globalSystemVersion, ref LookupCache lookupCache) + public static byte* GetComponentDataWithTypeRW(ChunkIndex chunk, Archetype* archetype, int baseEntityIndex, TypeIndex typeIndex, uint globalSystemVersion, ref LookupCache lookupCache) { if (Hint.Unlikely(lookupCache.Archetype != archetype)) lookupCache.Update(archetype, typeIndex); // Write Component to Chunk. ChangeVersion:Yes OrderVersion:No - chunk->SetChangeVersion(lookupCache.IndexInArchetype, globalSystemVersion); - return chunk->Buffer + (lookupCache.ComponentOffset + lookupCache.ComponentSizeOf * baseEntityIndex); + archetype->Chunks.SetChangeVersion(lookupCache.IndexInArchetype, chunk.ListIndex, globalSystemVersion); + return chunk.Buffer + (lookupCache.ComponentOffset + lookupCache.ComponentSizeOf * baseEntityIndex); + } + + // This variant returns an invalid pointer if the component is not present. + // If you'd like a null pointer in this case instead, use GetOptionalComponentDataWithTypeRW() + public static byte* GetComponentDataWithTypeRW(ChunkIndex chunk, int baseEntityIndex, uint globalSystemVersion, ref LookupCache lookupCache) + { + // Write Component to Chunk. ChangeVersion:Yes OrderVersion:No + lookupCache.Archetype->Chunks.SetChangeVersion(lookupCache.IndexInArchetype, chunk.ListIndex, globalSystemVersion); + return chunk.Buffer + (lookupCache.ComponentOffset + lookupCache.ComponentSizeOf * baseEntityIndex); } // This variant returns null if the component is not present. - public static byte* GetOptionalComponentDataWithTypeRO(Chunk* chunk, Archetype* archetype, int baseEntityIndex, TypeIndex typeIndex, ref LookupCache lookupCache) + public static byte* GetOptionalComponentDataWithTypeRO(ChunkIndex chunk, Archetype* archetype, int baseEntityIndex, TypeIndex typeIndex, ref LookupCache lookupCache) { if (Hint.Unlikely(lookupCache.Archetype != archetype)) lookupCache.Update(archetype, typeIndex); if (Hint.Unlikely(lookupCache.IndexInArchetype == -1)) return null; - return chunk->Buffer + (lookupCache.ComponentOffset + lookupCache.ComponentSizeOf * baseEntityIndex); + return chunk.Buffer + (lookupCache.ComponentOffset + lookupCache.ComponentSizeOf * baseEntityIndex); } // This variant returns null if the component is not present. - public static byte* GetOptionalComponentDataWithTypeRW(Chunk* chunk, Archetype* archetype, int baseEntityIndex, TypeIndex typeIndex, uint globalSystemVersion, ref LookupCache lookupCache) + public static byte* GetOptionalComponentDataWithTypeRW(ChunkIndex chunk, Archetype* archetype, int baseEntityIndex, TypeIndex typeIndex, uint globalSystemVersion, ref LookupCache lookupCache) { if (Hint.Unlikely(lookupCache.Archetype != archetype)) lookupCache.Update(archetype, typeIndex); @@ -189,83 +198,79 @@ public static void SetSharedComponentDataIndex(EntityBatchInChunk batch, Archety return null; // Write Component to Chunk. ChangeVersion:Yes OrderVersion:No - chunk->SetChangeVersion(lookupCache.IndexInArchetype, globalSystemVersion); - return chunk->Buffer + (lookupCache.ComponentOffset + lookupCache.ComponentSizeOf * baseEntityIndex); + archetype->Chunks.SetChangeVersion(lookupCache.IndexInArchetype, chunk.ListIndex, globalSystemVersion); + return chunk.Buffer + (lookupCache.ComponentOffset + lookupCache.ComponentSizeOf * baseEntityIndex); } - public static byte* GetComponentDataWithTypeRO(Chunk* chunk, int baseEntityIndex, TypeIndex typeIndex) + public static byte* GetComponentDataWithTypeRO(ChunkIndex chunk, Archetype* archetype, int baseEntityIndex, TypeIndex typeIndex) { - var indexInTypeArray = GetIndexInTypeArray(chunk->Archetype, typeIndex); + var indexInTypeArray = GetIndexInTypeArray(archetype, typeIndex); - var offset = chunk->Archetype->Offsets[indexInTypeArray]; - var sizeOf = chunk->Archetype->SizeOfs[indexInTypeArray]; + var offset = archetype->Offsets[indexInTypeArray]; + var sizeOf = archetype->SizeOfs[indexInTypeArray]; - return chunk->Buffer + (offset + sizeOf * baseEntityIndex); + return chunk.Buffer + (offset + sizeOf * baseEntityIndex); } - public static byte* GetComponentDataWithTypeRW(Chunk* chunk, int baseEntityIndex, TypeIndex typeIndex, uint globalSystemVersion) + public static byte* GetComponentDataWithTypeRW(ChunkIndex chunk, Archetype* archetype, int baseEntityIndex, TypeIndex typeIndex, uint globalSystemVersion) { - var indexInTypeArray = GetIndexInTypeArray(chunk->Archetype, typeIndex); + var indexInTypeArray = GetIndexInTypeArray(archetype, typeIndex); - var offset = chunk->Archetype->Offsets[indexInTypeArray]; - var sizeOf = chunk->Archetype->SizeOfs[indexInTypeArray]; + var offset = archetype->Offsets[indexInTypeArray]; + var sizeOf = archetype->SizeOfs[indexInTypeArray]; // Write Component to Chunk. ChangeVersion:Yes OrderVersion:No - chunk->SetChangeVersion(indexInTypeArray, globalSystemVersion); + archetype->Chunks.SetChangeVersion(indexInTypeArray, chunk.ListIndex, globalSystemVersion); - return chunk->Buffer + (offset + sizeOf * baseEntityIndex); + return chunk.Buffer + (offset + sizeOf * baseEntityIndex); } - public static byte* GetComponentDataRO(Chunk* chunk, int baseEntityIndex, int indexInTypeArray) + public static byte* GetComponentDataRO(ChunkIndex chunk, Archetype* archetype, int baseEntityIndex, int indexInTypeArray) { - var offset = chunk->Archetype->Offsets[indexInTypeArray]; - var sizeOf = chunk->Archetype->SizeOfs[indexInTypeArray]; + var offset = archetype->Offsets[indexInTypeArray]; + var sizeOf = archetype->SizeOfs[indexInTypeArray]; - return chunk->Buffer + (offset + sizeOf * baseEntityIndex); + return chunk.Buffer + (offset + sizeOf * baseEntityIndex); } - public static byte* GetComponentDataRW(Chunk* chunk, int baseEntityIndex, int indexInTypeArray, uint globalSystemVersion) + public static byte* GetComponentDataRW(ChunkIndex chunk, Archetype* archetype, int baseEntityIndex, int indexInTypeArray, uint globalSystemVersion) { - var offset = chunk->Archetype->Offsets[indexInTypeArray]; - var sizeOf = chunk->Archetype->SizeOfs[indexInTypeArray]; + var offset = archetype->Offsets[indexInTypeArray]; + var sizeOf = archetype->SizeOfs[indexInTypeArray]; // Write Component to Chunk. ChangeVersion:Yes OrderVersion:No - chunk->SetChangeVersion(indexInTypeArray, globalSystemVersion); + archetype->Chunks.SetChangeVersion(indexInTypeArray, chunk.ListIndex, globalSystemVersion); - return chunk->Buffer + (offset + sizeOf * baseEntityIndex); + return chunk.Buffer + (offset + sizeOf * baseEntityIndex); } - public static UnsafeBitArray GetEnabledRefRO(Chunk* chunk, int indexInTypeArray) + public static UnsafeBitArray GetEnabledRefRO(ChunkIndex chunk, Archetype* archetype, int indexInTypeArray) { - var archetype = chunk->Archetype; var chunks = archetype->Chunks; int memoryOrderIndexInArchetype = archetype->TypeIndexInArchetypeToMemoryOrderIndex[indexInTypeArray]; - return chunks.GetEnabledArrayForTypeInChunk(memoryOrderIndexInArchetype, chunk->ListIndex); + return chunks.GetEnabledArrayForTypeInChunk(memoryOrderIndexInArchetype, chunk.ListIndex); } - public static UnsafeBitArray GetEnabledRefRW(Chunk* chunk, int indexInTypeArray, - out int* ptrChunkDisabledCount) - => GetEnabledRefRW(chunk, indexInTypeArray, - chunk->Archetype->EntityComponentStore->GlobalSystemVersion, out ptrChunkDisabledCount); - - public static UnsafeBitArray GetEnabledRefRW(Chunk* chunk, int indexInTypeArray, uint globalSystemVersion, out int* ptrChunkDisabledCount) + public static UnsafeBitArray GetEnabledRefRW(ChunkIndex chunk, Archetype* archetype, int indexInTypeArray, uint globalSystemVersion, out int* ptrChunkDisabledCount) { - chunk->SetChangeVersion(indexInTypeArray, globalSystemVersion); - - var archetype = chunk->Archetype; + var chunkListIndex = chunk.ListIndex; var chunks = archetype->Chunks; + + chunks.SetChangeVersion(indexInTypeArray, chunkListIndex, globalSystemVersion); + int memoryOrderIndexInArchetype = archetype->TypeIndexInArchetypeToMemoryOrderIndex[indexInTypeArray]; - ptrChunkDisabledCount = chunks.GetPointerToChunkDisabledCountForType(memoryOrderIndexInArchetype, chunk->ListIndex); - return chunks.GetEnabledArrayForTypeInChunk(memoryOrderIndexInArchetype, chunk->ListIndex); + ptrChunkDisabledCount = chunks.GetPointerToChunkDisabledCountForType(memoryOrderIndexInArchetype, chunkListIndex); + return chunks.GetEnabledArrayForTypeInChunk(memoryOrderIndexInArchetype, chunkListIndex); } - public static void Copy(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int dstIndex, int count) + public static void Copy(EntityComponentStore* entityComponentStore, ChunkIndex srcChunk, int srcIndex, ChunkIndex dstChunk, int dstIndex, int count) { - Assert.IsTrue(srcChunk->Archetype == dstChunk->Archetype); + var arch = entityComponentStore->GetArchetype(srcChunk); + + Assert.IsTrue(arch == entityComponentStore->GetArchetype(dstChunk)); - var arch = srcChunk->Archetype; - var srcBuffer = srcChunk->Buffer; - var dstBuffer = dstChunk->Buffer; + var srcBuffer = srcChunk.Buffer; + var dstBuffer = dstChunk.Buffer; var offsets = arch->Offsets; var sizeOfs = arch->SizeOfs; var typesCount = arch->TypesCount; @@ -281,16 +286,15 @@ public static void Copy(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int dstI } } - // Assumes that chunk->Count is valid - public static void UpdateChunkDisabledEntityCounts(Chunk* chunk) + public static void UpdateChunkDisabledEntityCounts(ChunkIndex chunk, Archetype* arch) { - var arch = chunk->Archetype; - int chunkIndexInArchetype = chunk->ListIndex; - int chunkEntityCount = chunk->Count; - var chunkTypeEnabledMasks = arch->Chunks.GetComponentEnabledMaskArrayForChunk(chunkIndexInArchetype); - var chunkTypeDisabledCounts = arch->Chunks.GetChunkDisabledCounts(chunkIndexInArchetype); - for (var t = 0; t < arch->TypesCount; t++) + int chunkIndexInArchetype = chunk.ListIndex; + int chunkEntityCount = chunk.Count; + var archetypeChunkData = arch->Chunks; + var chunkTypeEnabledMasks = archetypeChunkData.GetComponentEnabledMaskArrayForChunk(chunkIndexInArchetype); + var chunkTypeDisabledCounts = archetypeChunkData.GetChunkDisabledCounts(chunkIndexInArchetype); + for (int t = 0, count = arch->TypesCount; t < count; t++) { int chunkEnabledCount = EnabledBitUtility.countbits(chunkTypeEnabledMasks[t]); chunkTypeDisabledCounts[t] = chunkEntityCount - chunkEnabledCount; @@ -298,12 +302,10 @@ public static void UpdateChunkDisabledEntityCounts(Chunk* chunk) } // Use this when cloning entities inside of a chunk to either the same chunk or a different chunk - public static void CloneEnabledBits(Chunk* srcChunk, int srcEntityIndexInChunk, Chunk* dstChunk, int dstEntityIndexInChunk, int entityCount) + public static void CloneEnabledBits(ChunkIndex srcChunk, Archetype* srcArch, int srcEntityIndexInChunk, ChunkIndex dstChunk, Archetype* dstArch, int dstEntityIndexInChunk, int entityCount) { - var srcArch = srcChunk->Archetype; - var dstArch = dstChunk->Archetype; - int srcChunkIndexInArchetype = srcChunk->ListIndex; - int dstChunkIndexInArchetype = dstChunk->ListIndex; + int srcChunkIndexInArchetype = srcChunk.ListIndex; + int dstChunkIndexInArchetype = dstChunk.ListIndex; var srcEnableableTypesCount = srcArch->EnableableTypesCount; var dstTypeIndexInArchetype = 0; @@ -328,18 +330,19 @@ public static void CloneEnabledBits(Chunk* srcChunk, int srcEntityIndexInChunk, } // Use this when instantiating a single entity many times - public static void ReplicateEnabledBits(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int dstIndex, int count) + public static void ReplicateEnabledBits(ChunkIndex srcChunk, Archetype* srcArch, int srcIndex, ChunkIndex dstChunk, Archetype* dstArch, int dstIndex, int count) { - var srcArch = srcChunk->Archetype; - var dstArch = dstChunk->Archetype; var srcEnableableTypesCount = srcArch->EnableableTypesCount; int dstIndexInArchetype = 0; + var srcChunkListIndex = srcChunk.ListIndex; + var dstChunkListIndex = dstChunk.ListIndex; + for (var t = 0; t < srcEnableableTypesCount; t++) { var srcIndexInArchetype = srcArch->EnableableTypeIndexInArchetype[t]; int srcMemoryOrderIndexInArchetype = srcArch->TypeIndexInArchetypeToMemoryOrderIndex[srcIndexInArchetype]; - var srcBits = srcArch->Chunks.GetEnabledArrayForTypeInChunk(srcMemoryOrderIndexInArchetype, srcChunk->ListIndex); + var srcBits = srcArch->Chunks.GetEnabledArrayForTypeInChunk(srcMemoryOrderIndexInArchetype, srcChunkListIndex); var srcValue = srcBits.IsSet(srcIndex); int result = GetNextIndexInTypeArray(dstArch, srcArch->Types[srcIndexInArchetype].TypeIndex, dstIndexInArchetype); @@ -348,13 +351,13 @@ public static void ReplicateEnabledBits(Chunk* srcChunk, int srcIndex, Chunk* ds dstIndexInArchetype = result; int dstMemoryOrderIndexInArchetype = dstArch->TypeIndexInArchetypeToMemoryOrderIndex[dstIndexInArchetype]; - var dstBits = dstArch->Chunks.GetEnabledArrayForTypeInChunk(dstMemoryOrderIndexInArchetype, dstChunk->ListIndex); + var dstBits = dstArch->Chunks.GetEnabledArrayForTypeInChunk(dstMemoryOrderIndexInArchetype, dstChunkListIndex); dstBits.SetBits(dstIndex, srcValue, count); // If the src value is disabled, adjust the hierarchical data if (!srcValue) { - dstArch->Chunks.AdjustChunkDisabledCountForType(dstMemoryOrderIndexInArchetype, dstChunk->ListIndex, count); + dstArch->Chunks.AdjustChunkDisabledCountForType(dstMemoryOrderIndexInArchetype, dstChunkListIndex, count); } } } @@ -403,16 +406,16 @@ public static void MoveEnabledBits(Archetype* srcArchetype, int srcChunkIndex, A InitializeBitsForNewChunk(srcArchetype, srcChunkIndex); } - public static void ClearPaddingBits(Chunk* chunk, int startIndex, int count) + public static void ClearPaddingBits(ChunkIndex chunk, Archetype* archetype, int startIndex, int count) { - var archetype = chunk->Archetype; var enableableTypesCount = archetype->EnableableTypesCount; + var chunkListIndex = chunk.ListIndex; for (var t = 0; t < enableableTypesCount; t++) { var indexInArchetype = archetype->EnableableTypeIndexInArchetype[t]; int memoryOrderIndexInArchetype = archetype->TypeIndexInArchetypeToMemoryOrderIndex[indexInArchetype]; - var bits = archetype->Chunks.GetEnabledArrayForTypeInChunk(memoryOrderIndexInArchetype, chunk->ListIndex); + var bits = archetype->Chunks.GetEnabledArrayForTypeInChunk(memoryOrderIndexInArchetype, chunkListIndex); bits.SetBits(startIndex, false, count); } } @@ -426,34 +429,32 @@ public static void InitializeBitsForNewChunk(Archetype* archetype, int chunkInde archetype->Chunks.InitializeDisabledCountForChunk(chunkIndex); } - public static void RemoveFromEnabledBitsHierarchicalData(Chunk* chunk, int startIndex, int count) + public static void RemoveFromEnabledBitsHierarchicalData(ChunkIndex chunk, Archetype* archetype, int startIndex, int count) { - var archetype = chunk->Archetype; var enableableTypesCount = archetype->EnableableTypesCount; + var chunkListIndex = chunk.ListIndex; for (var t = 0; t < enableableTypesCount; t++) { var indexInArchetype = archetype->EnableableTypeIndexInArchetype[t]; int memoryOrderIndexInArchetype = archetype->TypeIndexInArchetypeToMemoryOrderIndex[indexInArchetype]; - var bits = archetype->Chunks.GetEnabledArrayForTypeInChunk(memoryOrderIndexInArchetype, chunk->ListIndex); + var bits = archetype->Chunks.GetEnabledArrayForTypeInChunk(memoryOrderIndexInArchetype, chunkListIndex); var removedDisabledCount = count - bits.CountBits(startIndex, count); - archetype->Chunks.AdjustChunkDisabledCountForType(memoryOrderIndexInArchetype, chunk->ListIndex, -removedDisabledCount); + archetype->Chunks.AdjustChunkDisabledCountForType(memoryOrderIndexInArchetype, chunkListIndex, -removedDisabledCount); } } - public static void CopyComponents(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int dstIndex, int count, uint dstGlobalSystemVersion) + public static void CopyComponents(ChunkIndex srcChunk, Archetype* srcArch, int srcIndex, ChunkIndex dstChunk, Archetype* dstArch, int dstIndex, int count, uint dstGlobalSystemVersion) { - var srcArch = srcChunk->Archetype; var typesCount = srcArch->TypesCount; #if UNITY_ASSERTIONS // This function is used to swap data between different world so assert that the layout is identical if // the archetypes dont match - var dstArch = dstChunk->Archetype; if (srcArch != dstArch) { - Assert.AreEqual(typesCount, dstChunk->Archetype->TypesCount); + Assert.AreEqual(typesCount, dstArch->TypesCount); for (int i = 0; i < typesCount; ++i) { Assert.AreEqual(srcArch->Types[i], dstArch->Types[i]); @@ -463,11 +464,13 @@ public static void CopyComponents(Chunk* srcChunk, int srcIndex, Chunk* dstChunk } #endif - var srcBuffer = srcChunk->Buffer; - var dstBuffer = dstChunk->Buffer; + var srcBuffer = srcChunk.Buffer; + var dstBuffer = dstChunk.Buffer; var offsets = srcArch->Offsets; var sizeOfs = srcArch->SizeOfs; + var dstChunkListIndex = dstChunk.ListIndex; + for (var t = 1; t < typesCount; t++) // Only copy component data, not Entity { var offset = offsets[t]; @@ -475,26 +478,23 @@ public static void CopyComponents(Chunk* srcChunk, int srcIndex, Chunk* dstChunk var src = srcBuffer + (offset + sizeOf * srcIndex); var dst = dstBuffer + (offset + sizeOf * dstIndex); - dstChunk->SetChangeVersion(t, dstGlobalSystemVersion); + dstArch->Chunks.SetChangeVersion(t, dstChunkListIndex, dstGlobalSystemVersion); UnsafeUtility.MemCpy(dst, src, sizeOf * count); } } [BurstCompile] - public static void SwapComponents(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int dstIndex, int count, uint srcGlobalSystemVersion, uint dstGlobalSystemVersion) + public static void SwapComponents(Archetype* srcArch, ChunkIndex srcChunk, int srcIndex, Archetype* dstArch, ChunkIndex dstChunk, int dstIndex, int count, uint srcGlobalSystemVersion, uint dstGlobalSystemVersion) { - var srcArch = srcChunk->Archetype; var typesCount = srcArch->TypesCount; - #if UNITY_ASSERTIONS // This function is used to swap data between different world so assert that the layout is identical if // the archetypes dont match - var dstArch = dstChunk->Archetype; if (srcArch != dstArch) { - Assert.AreEqual(typesCount, dstChunk->Archetype->TypesCount); + Assert.AreEqual(typesCount, dstArch->TypesCount); for (int i = 0; i < typesCount; ++i) { Assert.AreEqual(srcArch->Types[i], dstArch->Types[i]); @@ -504,11 +504,14 @@ public static void SwapComponents(Chunk* srcChunk, int srcIndex, Chunk* dstChunk } #endif - var srcBuffer = srcChunk->Buffer; - var dstBuffer = dstChunk->Buffer; + var srcBuffer = srcChunk.Buffer; + var dstBuffer = dstChunk.Buffer; var offsets = srcArch->Offsets; var sizeOfs = srcArch->SizeOfs; + var dstChunkListIndex = dstChunk.ListIndex; + var srcChunkListIndex = srcChunk.ListIndex; + for (var t = 1; t < typesCount; t++) // Only swap component data, not Entity { var offset = offsets[t]; @@ -517,8 +520,8 @@ public static void SwapComponents(Chunk* srcChunk, int srcIndex, Chunk* dstChunk var dst = dstBuffer + (offset + sizeOf * dstIndex); Byte* buffer = stackalloc Byte[sizeOf * count]; - dstChunk->SetChangeVersion(t, dstGlobalSystemVersion); - srcChunk->SetChangeVersion(t, srcGlobalSystemVersion); + dstArch->Chunks.SetChangeVersion(t, dstChunkListIndex, dstGlobalSystemVersion); + srcArch->Chunks.SetChangeVersion(t, srcChunkListIndex, srcGlobalSystemVersion); UnsafeUtility.MemCpy(buffer, src, sizeOf * count); UnsafeUtility.MemCpy(src, dst, sizeOf * count); @@ -526,14 +529,12 @@ public static void SwapComponents(Chunk* srcChunk, int srcIndex, Chunk* dstChunk } } - public static void InitializeComponents(Chunk* dstChunk, int dstIndex, int count) + public static void InitializeComponents(Archetype* arch, ChunkIndex dstChunk, int dstIndex, int count) { - var arch = dstChunk->Archetype; - var offsets = arch->Offsets; var sizeOfs = arch->SizeOfs; var bufferCapacities = arch->BufferCapacities; - var dstBuffer = dstChunk->Buffer; + var dstBuffer = dstChunk.Buffer; var typesCount = arch->TypesCount; var types = arch->Types; @@ -564,7 +565,7 @@ public static void InitializeComponents(Chunk* dstChunk, int dstIndex, int count { var indexInArchetype = arch->EnableableTypeIndexInArchetype[t]; int memoryOrderIndexInArchetype = arch->TypeIndexInArchetypeToMemoryOrderIndex[indexInArchetype]; - var bits = arch->Chunks.GetEnabledArrayForTypeInChunk(memoryOrderIndexInArchetype, dstChunk->ListIndex); + var bits = arch->Chunks.GetEnabledArrayForTypeInChunk(memoryOrderIndexInArchetype, dstChunk.ListIndex); bits.SetBits(dstIndex, true, count); } @@ -579,11 +580,9 @@ public static void InitializeBuffersInChunk(byte* p, int count, int stride, int } } - public static void Convert(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int dstIndex, int count) + public static void Convert(Archetype* srcArch, ChunkIndex srcChunk, int srcIndex, Archetype* dstArch, ChunkIndex dstChunk, int dstIndex, int count) { - Assert.IsFalse(srcChunk == dstChunk); - var srcArch = srcChunk->Archetype; - var dstArch = dstChunk->Archetype; + Assert.AreNotEqual(srcChunk, dstChunk); var entityComponentStore = dstArch->EntityComponentStore; if (srcArch != dstArch) { @@ -597,6 +596,9 @@ public static void Convert(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int d var sourceTypesToDealloc = stackalloc int[srcI + 1]; int sourceTypesToDeallocCount = 0; + var srcChunkBuffer = srcChunk.Buffer; + var dstChunkBuffer = dstChunk.Buffer; + while (dstI >= 0) { var srcType = srcArch->Types[srcI]; @@ -612,8 +614,8 @@ public static void Convert(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int d var srcStride = srcArch->SizeOfs[srcI]; var dstStride = dstArch->SizeOfs[dstI]; - var src = srcChunk->Buffer + srcArch->Offsets[srcI] + srcIndex * srcStride; - var dst = dstChunk->Buffer + dstArch->Offsets[dstI] + dstIndex * dstStride; + var src = srcChunkBuffer + srcArch->Offsets[srcI] + srcIndex * srcStride; + var dst = dstChunkBuffer + dstArch->Offsets[dstI] + dstIndex * dstStride; if (srcType == dstType) { @@ -633,7 +635,7 @@ public static void Convert(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int d if (dstType.IsEnableable) { int dstMemoryOrderIndexInArchetype = dstArch->TypeIndexInArchetypeToMemoryOrderIndex[dstI]; - var bits = dstArch->Chunks.GetEnabledArrayForTypeInChunk(dstMemoryOrderIndexInArchetype, dstChunk->ListIndex); + var bits = dstArch->Chunks.GetEnabledArrayForTypeInChunk(dstMemoryOrderIndexInArchetype, dstChunk.ListIndex); bits.SetBits(dstIndex, true, count); } --dstI; @@ -671,7 +673,7 @@ public static void Convert(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int d if (dstType.IsEnableable) { int dstMemoryOrderIndexInArchetype = dstArch->TypeIndexInArchetypeToMemoryOrderIndex[dstI]; - var bits = dstArch->Chunks.GetEnabledArrayForTypeInChunk(dstMemoryOrderIndexInArchetype, dstChunk->ListIndex); + var bits = dstArch->Chunks.GetEnabledArrayForTypeInChunk(dstMemoryOrderIndexInArchetype, dstChunk.ListIndex); bits.SetBits(dstIndex, true, count); } --dstTagI; @@ -691,7 +693,7 @@ public static void Convert(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int d { srcI = sourceTypesToDealloc[iDealloc]; var srcStride = srcArch->SizeOfs[srcI]; - var src = srcChunk->Buffer + srcArch->Offsets[srcI] + srcIndex * srcStride; + var src = srcChunkBuffer + srcArch->Offsets[srcI] + srcIndex * srcStride; var a = (int*)src; for (int i = 0; i < count; i++) @@ -711,7 +713,7 @@ public static void Convert(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int d { srcI = sourceTypesToDealloc[iDealloc]; var srcStride = srcArch->SizeOfs[srcI]; - var srcPtr = srcChunk->Buffer + srcArch->Offsets[srcI] + srcIndex * srcStride; + var srcPtr = srcChunkBuffer + srcArch->Offsets[srcI] + srcIndex * srcStride; for (int i = 0; i < count; i++) { BufferHeader.Destroy((BufferHeader*)srcPtr); @@ -721,26 +723,22 @@ public static void Convert(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int d } } - public static void MemsetUnusedChunkData(Chunk* chunk, byte value) + public static void MemsetUnusedChunkData(Archetype* arch, byte* chunkBuffer, byte value, int entityCount) { - var arch = chunk->Archetype; - var buffer = chunk->Buffer; - var count = chunk->Count; - // Clear unused buffer data for (int i = 0; i < arch->TypesCount; ++i) { - var componentDataType = &arch->Types[i]; + var componentDataType = arch->Types[i]; var componentSize = arch->SizeOfs[i]; - if (componentDataType->IsBuffer) + if (componentDataType.IsBuffer) { - var elementSize = TypeManager.GetTypeInfo(componentDataType->TypeIndex).ElementSize; + var elementSize = TypeManager.GetTypeInfo(componentDataType.TypeIndex).ElementSize; var bufferCapacity = arch->BufferCapacities[i]; - for (int chunkI = 0; chunkI < count; chunkI++) + for (int chunkI = 0; chunkI < entityCount; chunkI++) { - var bufferHeader = (BufferHeader*)(buffer + arch->Offsets[i] + (chunkI * componentSize)); + var bufferHeader = (BufferHeader*)(chunkBuffer + arch->Offsets[i] + chunkI * componentSize); BufferHeader.MemsetUnusedMemory(bufferHeader, bufferCapacity, elementSize, value); } } @@ -753,22 +751,21 @@ public static void MemsetUnusedChunkData(Chunk* chunk, byte value) var nextIndex = arch->TypeMemoryOrderIndexToIndexInArchetype[i + 1]; var componentSize = arch->SizeOfs[index]; - var startOffset = arch->Offsets[index] + count * componentSize; + var startOffset = arch->Offsets[index] + entityCount * componentSize; var endOffset = arch->Offsets[nextIndex]; Assert.AreNotEqual(-1, startOffset); Assert.AreNotEqual(-1, endOffset); - Assert.IsTrue(componentSize >= 0); - UnsafeUtility.MemSet(buffer + startOffset, value, endOffset - startOffset); + UnsafeUtility.MemSet(chunkBuffer + startOffset, value, endOffset - startOffset); } var lastIndex = arch->TypeMemoryOrderIndexToIndexInArchetype[arch->TypesCount - 1]; - var lastStartOffset = arch->Offsets[lastIndex] + count * arch->SizeOfs[lastIndex]; - var bufferSize = Chunk.GetChunkBufferSize(); - UnsafeUtility.MemSet(buffer + lastStartOffset, value, bufferSize - lastStartOffset); + var lastStartOffset = arch->Offsets[lastIndex] + entityCount * arch->SizeOfs[lastIndex]; + var bufferSize = Chunk.kChunkBufferSize; + UnsafeUtility.MemSet(chunkBuffer + lastStartOffset, value, bufferSize - lastStartOffset); // clear the chunk header padding zone - UnsafeUtility.MemClear(Chunk.kSerializedHeaderSize + (byte*)chunk, Chunk.kBufferOffset - Chunk.kSerializedHeaderSize); + UnsafeUtility.MemSet(chunkBuffer - Chunk.kBufferOffset + Chunk.kSerializedHeaderSize, value, Chunk.kBufferOffset - Chunk.kSerializedHeaderSize); } public static bool AreLayoutCompatible(Archetype* a, Archetype* b) @@ -806,18 +803,19 @@ public static void AssertAreLayoutCompatible(Archetype* a, Archetype* b) } } - public static void DeallocateBuffers(Chunk* chunk) + public static void DeallocateBuffers(Archetype* archetype, ChunkIndex chunk) { - var archetype = chunk->Archetype; - var bufferComponentsEnd = archetype->BufferComponentsEnd; + var buffer = chunk.Buffer; + var entityCount = chunk.Count; + for (var ti = archetype->FirstBufferComponent; ti < bufferComponentsEnd; ++ti) { Assert.IsTrue(archetype->Types[ti].IsBuffer); - var basePtr = chunk->Buffer + archetype->Offsets[ti]; + var basePtr = buffer + archetype->Offsets[ti]; var stride = archetype->SizeOfs[ti]; - for (int i = 0; i < chunk->Count; ++i) + for (int i = 0; i < entityCount; ++i) { byte* bufferPtr = basePtr + stride * i; BufferHeader.Destroy((BufferHeader*)bufferPtr); @@ -825,15 +823,14 @@ public static void DeallocateBuffers(Chunk* chunk) } } - static void ReleaseChunk(Chunk* chunk) + static void ReleaseChunk(Archetype* archetype, ChunkIndex chunk) { - var archetype = chunk->Archetype; var entityComponentStore = archetype->EntityComponentStore; // Remove references to shared components - if (chunk->Archetype->NumSharedComponents > 0) + if (archetype->NumSharedComponents > 0) { - var sharedComponentValueArray = chunk->SharedComponentValues; + var sharedComponentValueArray = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); for (var i = 0; i < archetype->NumSharedComponents; ++i) { @@ -848,115 +845,112 @@ static void ReleaseChunk(Chunk* chunk) } // this chunk is going away, so it shouldn't be in the empty slot list. - if (chunk->Count < chunk->Capacity) + if (chunk.Count < archetype->ChunkCapacity) archetype->EmptySlotTrackingRemoveChunk(chunk); - chunk->Archetype->RemoveFromChunkList(chunk, ref entityComponentStore->m_ChunkListChangesTracker); - chunk->Archetype = null; + archetype->RemoveFromChunkList(chunk, ref entityComponentStore->m_ChunkListChangesTracker); + entityComponentStore->SetArchetype(chunk, null); entityComponentStore->FreeChunk(chunk); } - public static void SetChunkCountKeepMetaChunk(Chunk* chunk, int newCount) + public static void SetChunkCountKeepMetaChunk(Archetype* archetype, ChunkIndex chunk, int newCount) { - Assert.AreNotEqual(newCount, chunk->Count); + Assert.AreNotEqual(newCount, chunk.Count); // Chunk released to empty chunk pool if (newCount == 0) { - ReleaseChunk(chunk); + ReleaseChunk(archetype, chunk); return; } - var capacity = chunk->Capacity; + var capacity = archetype->ChunkCapacity; // Chunk is now full if (newCount == capacity) { // this chunk no longer has empty slots, so it shouldn't be in the empty slot list. - chunk->Archetype->EmptySlotTrackingRemoveChunk(chunk); + archetype->EmptySlotTrackingRemoveChunk(chunk); } // Chunk is no longer full - else if (chunk->Count == capacity) + else if (chunk.Count == capacity) { - Assert.IsTrue(newCount < chunk->Count); - chunk->Archetype->EmptySlotTrackingAddChunk(chunk); + Assert.IsTrue(newCount < chunk.Count); + archetype->EmptySlotTrackingAddChunk(chunk); } - chunk->Count = newCount; - chunk->Archetype->Chunks.SetChunkEntityCount(chunk->ListIndex, newCount); + chunk.Count = newCount; + archetype->Chunks.SetChunkEntityCount(chunk.ListIndex, newCount); } - public static void SetChunkCount(Chunk* chunk, int newCount) + public static void SetChunkCount(Archetype* archetype, ChunkIndex chunk, int newCount) { - var archetype = chunk->Archetype; var entityComponentStore = archetype->EntityComponentStore; - var metaChunkEntity = chunk->metaChunkEntity; - if (newCount == 0 && metaChunkEntity != Entity.Null) - entityComponentStore->DestroyMetaChunkEntity(metaChunkEntity); + if (newCount == 0 && archetype->MetaChunkArchetype != null) + { + entityComponentStore->DestroyMetaChunkEntity(chunk.MetaChunkEntity); + } - SetChunkCountKeepMetaChunk(chunk, newCount); + SetChunkCountKeepMetaChunk(archetype, chunk, newCount); } // #todo DOTS-1189 - static int AllocateIntoChunk(Chunk* chunk, int count, out int outIndex) + static int AllocateIntoChunk(Archetype* archetype, ChunkIndex chunk, int count, out int outIndex) { - var allocatedCount = Math.Min(chunk->Capacity - chunk->Count, count); - outIndex = chunk->Count; - SetChunkCount(chunk, chunk->Count + allocatedCount); - chunk->Archetype->EntityCount += allocatedCount; + outIndex = chunk.Count; + var allocatedCount = Math.Min(archetype->ChunkCapacity - outIndex, count); + SetChunkCount(archetype, chunk, outIndex + allocatedCount); + archetype->EntityCount += allocatedCount; return allocatedCount; } - public static void Allocate(Chunk* chunk, int count) + public static void Allocate(Archetype* archetype, ChunkIndex chunk, int count) { - Allocate(chunk, null, count); + Allocate(archetype, chunk, null, count); } - public static void Allocate(Chunk* chunk, Entity* entities, int count) + public static void Allocate(Archetype* archetype, ChunkIndex chunk, Entity* entities, int count) { - var archetype = chunk->Archetype; var entityComponentStore = archetype->EntityComponentStore; var globalSystemVersion = entityComponentStore->GlobalSystemVersion; - int allocatedIndex; - var allocatedCount = AllocateIntoChunk(chunk, count, out allocatedIndex); + var allocatedCount = AllocateIntoChunk(archetype, chunk, count, out var allocatedIndex); entityComponentStore->AllocateEntities(archetype, chunk, allocatedIndex, allocatedCount, entities); - InitializeComponents(chunk, allocatedIndex, allocatedCount); + InitializeComponents(archetype, chunk, allocatedIndex, allocatedCount); // Add Entities in Chunk. ChangeVersion:Yes OrderVersion:Yes - chunk->SetAllChangeVersions(globalSystemVersion); - chunk->SetOrderVersion(globalSystemVersion); + archetype->Chunks.SetAllChangeVersion(chunk.ListIndex, globalSystemVersion); + archetype->Chunks.SetOrderVersion(chunk.ListIndex, globalSystemVersion); entityComponentStore->IncrementComponentTypeOrderVersion(archetype); } - public static void Remove(in EntityBatchInChunk batchInChunk) + public static void Remove(EntityComponentStore* entityComponentStore, in EntityBatchInChunk batchInChunk) { var chunk = batchInChunk.Chunk; var count = batchInChunk.Count; var startIndex = batchInChunk.StartIndex; - var archetype = chunk->Archetype; - var entityComponentStore = archetype->EntityComponentStore; + var archetype = entityComponentStore->GetArchetype(chunk); // Fill in moved component data from the end. var srcTailIndex = startIndex + count; - var srcTailCount = chunk->Count - srcTailIndex; + var srcTailCount = chunk.Count - srcTailIndex; var fillCount = math.min(count, srcTailCount); if (fillCount > 0) { - var fillStartIndex = chunk->Count - fillCount; + var fillStartIndex = chunk.Count - fillCount; - Copy(chunk, fillStartIndex, chunk, startIndex, fillCount); + Copy(entityComponentStore, chunk, fillStartIndex, chunk, startIndex, fillCount); - RemoveFromEnabledBitsHierarchicalData(chunk, startIndex, count); - CloneEnabledBits(chunk, fillStartIndex, chunk, startIndex, fillCount); + RemoveFromEnabledBitsHierarchicalData(chunk, archetype, startIndex, count); + CloneEnabledBits(chunk, archetype, fillStartIndex, chunk, archetype, startIndex, fillCount); - var clearStartIndex = chunk->Count - count; - ClearPaddingBits(chunk, clearStartIndex, count); + var clearStartIndex = chunk.Count - count; + ClearPaddingBits(chunk, archetype, clearStartIndex, count); - var fillEntities = (Entity*)GetComponentDataRO(chunk, startIndex, 0); + var fillEntities = (Entity*)GetComponentDataRO(chunk, archetype, startIndex, 0); for (int i = 0; i < fillCount; i++) { var entity = fillEntities[i]; @@ -966,19 +960,20 @@ public static void Remove(in EntityBatchInChunk batchInChunk) else { // Need to clear bits for all of the entities we removed - RemoveFromEnabledBitsHierarchicalData(chunk, startIndex, count); - ClearPaddingBits(chunk, startIndex, count); + RemoveFromEnabledBitsHierarchicalData(chunk, archetype, startIndex, count); + ClearPaddingBits(chunk, archetype, startIndex, count); } - chunk->SetOrderVersion(entityComponentStore->GlobalSystemVersion); + archetype->Chunks.SetOrderVersion(chunk.ListIndex, entityComponentStore->GlobalSystemVersion); entityComponentStore->IncrementComponentTypeOrderVersion(archetype); - entityComponentStore->ManagedChangesTracker.IncrementComponentOrderVersion(archetype, chunk->SharedComponentValues); + var sharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); + entityComponentStore->ManagedChangesTracker.IncrementComponentOrderVersion(archetype, sharedComponentValues); - int newChunkEntityCount = chunk->Count - count; - SetChunkCount(chunk, newChunkEntityCount); + int newChunkEntityCount = chunk.Count - count; + SetChunkCount(archetype, chunk, newChunkEntityCount); if (fillCount > 0 && newChunkEntityCount != 0) // can't do this until the chunk count is updated and padding bits are clear - UpdateChunkDisabledEntityCounts(chunk); + UpdateChunkDisabledEntityCounts(chunk, archetype); archetype->EntityCount -= count; } @@ -986,12 +981,13 @@ public static void Remove(in EntityBatchInChunk batchInChunk) /// Fix-up the chunk to refer to a different (but layout compatible) archetype. /// - Should only be called by Move(chunk) /// - public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchetype, int* sharedComponentValues) + public static void ChangeArchetypeInPlace(Archetype* srcArchetype, ChunkIndex srcChunk, Archetype* dstArchetype, int* dstSharedComponentValues) { - var srcArchetype = srcChunk->Archetype; var entityComponentStore = dstArchetype->EntityComponentStore; AssertAreLayoutCompatible(srcArchetype, dstArchetype); + var srcSharedComponentValues = srcArchetype->Chunks.GetSharedComponentValues(srcChunk.ListIndex); + var fixupSharedComponentReferences = (srcArchetype->NumSharedComponents > 0) || (dstArchetype->NumSharedComponents > 0); if (fixupSharedComponentReferences) @@ -1010,8 +1006,8 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety var dstType = dstArchetype->Types[n + dstFirstShared].TypeIndex; if (srcType == dstType) { - var srcSharedComponentDataIndex = srcChunk->SharedComponentValues[o]; - var dstSharedComponentDataIndex = sharedComponentValues[n]; + var srcSharedComponentDataIndex = srcSharedComponentValues[o]; + var dstSharedComponentDataIndex = dstSharedComponentValues[n]; if (srcSharedComponentDataIndex != dstSharedComponentDataIndex) { if (EntityComponentStore.IsUnmanagedSharedComponentIndex(srcSharedComponentDataIndex)) @@ -1033,7 +1029,7 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety } else if (dstType > srcType) // removed from dstArchetype { - var sharedComponentDataIndex = srcChunk->SharedComponentValues[o]; + var sharedComponentDataIndex = srcSharedComponentValues[o]; if (EntityComponentStore.IsUnmanagedSharedComponentIndex(sharedComponentDataIndex)) { entityComponentStore->RemoveSharedComponentReference_Unmanaged(sharedComponentDataIndex); @@ -1044,7 +1040,7 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety } else // added to dstArchetype { - var sharedComponentDataIndex = sharedComponentValues[n]; + var sharedComponentDataIndex = dstSharedComponentValues[n]; if (EntityComponentStore.IsUnmanagedSharedComponentIndex(sharedComponentDataIndex)) { entityComponentStore->AddSharedComponentReference_Unmanaged(sharedComponentDataIndex); @@ -1057,7 +1053,7 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety for (; n < dstCount; n++) // added to dstArchetype { - var sharedComponentDataIndex = sharedComponentValues[n]; + var sharedComponentDataIndex = dstSharedComponentValues[n]; if (EntityComponentStore.IsUnmanagedSharedComponentIndex(sharedComponentDataIndex)) { entityComponentStore->AddSharedComponentReference_Unmanaged(sharedComponentDataIndex); @@ -1068,7 +1064,7 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety for (; o < srcCount; o++) // removed from dstArchetype { - var sharedComponentDataIndex = srcChunk->SharedComponentValues[o]; + var sharedComponentDataIndex = srcSharedComponentValues[o]; if (EntityComponentStore.IsUnmanagedSharedComponentIndex(sharedComponentDataIndex)) { entityComponentStore->RemoveSharedComponentReference_Unmanaged(sharedComponentDataIndex); @@ -1078,20 +1074,20 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety } } - var count = srcChunk->Count; - bool hasEmptySlots = count < srcChunk->Capacity; + var count = srcChunk.Count; + bool hasEmptySlots = count < srcArchetype->ChunkCapacity; if (hasEmptySlots) srcArchetype->EmptySlotTrackingRemoveChunk(srcChunk); - int chunkIndexInSrcArchetype = srcChunk->ListIndex; + int chunkIndexInSrcArchetype = srcChunk.ListIndex; if (Hint.Likely(dstArchetype != srcArchetype)) { //Change version is overriden below - dstArchetype->AddToChunkList(srcChunk, sharedComponentValues, 0, + dstArchetype->AddToChunkList(srcChunk, dstSharedComponentValues, 0, ref entityComponentStore->m_ChunkListChangesTracker); - int chunkIndexInDstArchetype = srcChunk->ListIndex; + int chunkIndexInDstArchetype = srcChunk.ListIndex; // For unchanged components: Copy versions from src to dst archetype // For different components: @@ -1104,9 +1100,9 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety InitializeBitsForNewChunk(dstArchetype, chunkIndexInDstArchetype); MoveEnabledBits(srcArchetype, chunkIndexInSrcArchetype, dstArchetype, chunkIndexInDstArchetype, count); - srcChunk->ListIndex = chunkIndexInSrcArchetype; + srcChunk.ListIndex = chunkIndexInSrcArchetype; srcArchetype->RemoveFromChunkList(srcChunk, ref entityComponentStore->m_ChunkListChangesTracker); - srcChunk->ListIndex = chunkIndexInDstArchetype; + srcChunk.ListIndex = chunkIndexInDstArchetype; srcArchetype->EntityCount -= count; dstArchetype->EntityCount += count; @@ -1115,7 +1111,7 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety // Bump the order versions. Even though the ORDER hasn't changed, the archetype HAS, which must be tracked. // Note that srcChunk is now in dstArchetype! // The order version for the chunk that moved must be updated. - dstArchetype->Chunks.SetOrderVersion(srcChunk->ListIndex, entityComponentStore->GlobalSystemVersion); + dstArchetype->Chunks.SetOrderVersion(srcChunk.ListIndex, entityComponentStore->GlobalSystemVersion); // The component type order version for all types in both the source and destination archetype must be incremented, // since entities with these types have moved. Types in both archetypes will have their version incremented twice, // but that's fine; the absolute value of the order version doesn't generally matter. It just needs to increase. @@ -1128,7 +1124,7 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety // We don't know which value changed at this point, so just copy them all. for (int i = 0, sharedComponentCount = srcArchetype->NumSharedComponents; i < sharedComponentCount; ++i) { - srcArchetype->Chunks.SetSharedComponentValue(i, chunkIndexInSrcArchetype, sharedComponentValues[i]); + srcArchetype->Chunks.SetSharedComponentValue(i, chunkIndexInSrcArchetype, dstSharedComponentValues[i]); } } @@ -1143,41 +1139,41 @@ public static void ChangeArchetypeInPlace(Chunk* srcChunk, Archetype* dstArchety } else if (dstArchetype->MetaChunkArchetype == null) { - entityComponentStore->DestroyMetaChunkEntity(srcChunk->metaChunkEntity); - srcChunk->metaChunkEntity = Entity.Null; + entityComponentStore->DestroyMetaChunkEntity(srcChunk.MetaChunkEntity); + srcChunk.MetaChunkEntity = Entity.Null; } else { - var metaChunk = entityComponentStore->GetChunk(srcChunk->metaChunkEntity); - entityComponentStore->Move(srcChunk->metaChunkEntity, dstArchetype->MetaChunkArchetype, metaChunk->SharedComponentValues); + var metaChunk = entityComponentStore->GetChunk(srcChunk.MetaChunkEntity); + var dstMetaSharedComponentValues = dstArchetype->Chunks.GetSharedComponentValues(metaChunk.ListIndex); + entityComponentStore->Move(srcChunk.MetaChunkEntity, dstArchetype->MetaChunkArchetype, dstMetaSharedComponentValues); } } } - public static void MoveArchetype(Chunk* chunk, Archetype* dstArchetype, SharedComponentValues sharedComponentValues) + public static void MoveArchetype(Archetype* srcArchetype, ChunkIndex chunk, Archetype* dstArchetype, SharedComponentValues sharedComponentValues) { - var srcArchetype = chunk->Archetype; var entityComponentStore = dstArchetype->EntityComponentStore; var globalSystemVersion = entityComponentStore->GlobalSystemVersion; - var count = chunk->Count; - bool hasEmptySlots = count < chunk->Capacity; + var count = chunk.Count; + bool hasEmptySlots = count < srcArchetype->ChunkCapacity; if (hasEmptySlots) srcArchetype->EmptySlotTrackingRemoveChunk(chunk); - int chunkIndexInSrcArchetype = chunk->ListIndex; + int chunkIndexInSrcArchetype = chunk.ListIndex; dstArchetype->AddToChunkList(chunk, sharedComponentValues, globalSystemVersion, ref entityComponentStore->m_ChunkListChangesTracker); - var chunkIndexInDstArchetype = chunk->ListIndex; + var chunkIndexInDstArchetype = chunk.ListIndex; // Since we're not getting a clean chunk, we need to initialize the new bits here InitializeBitsForNewChunk(dstArchetype, chunkIndexInDstArchetype); MoveEnabledBits(srcArchetype, chunkIndexInSrcArchetype, dstArchetype, chunkIndexInDstArchetype, count); - chunk->ListIndex = chunkIndexInSrcArchetype; + chunk.ListIndex = chunkIndexInSrcArchetype; srcArchetype->RemoveFromChunkList(chunk, ref entityComponentStore->m_ChunkListChangesTracker); - chunk->ListIndex = chunkIndexInDstArchetype; + chunk.ListIndex = chunkIndexInDstArchetype; entityComponentStore->SetArchetype(chunk, dstArchetype); @@ -1188,7 +1184,7 @@ public static void MoveArchetype(Chunk* chunk, Archetype* dstArchetype, SharedCo dstArchetype->EntityCount += count; entityComponentStore->IncrementComponentTypeOrderVersion(dstArchetype); - chunk->SetOrderVersion(globalSystemVersion); + dstArchetype->Chunks.SetOrderVersion(chunk.ListIndex, globalSystemVersion); } public static void CloneChangeVersions(Archetype* srcArchetype, int chunkIndexInSrcArchetype, Archetype* dstArchetype, int chunkIndexInDstArchetype, bool dstValidExistingVersions = false) @@ -1234,109 +1230,104 @@ public static void CloneChangeVersions(Archetype* srcArchetype, int chunkIndexIn } } - public static void AllocateClone(Chunk* chunk, Entity* entities, int count, Entity srcEntity) + public static void AllocateClone(Archetype* archetype, ChunkIndex chunk, Entity* entities, int count, Entity srcEntity) { - var archetype = chunk->Archetype; var entityComponentStore = archetype->EntityComponentStore; var globalSystemVersion = entityComponentStore->GlobalSystemVersion; var src = entityComponentStore->GetEntityInChunk(srcEntity); int allocatedIndex; - var allocatedCount = AllocateIntoChunk(chunk, count, out allocatedIndex); + var allocatedCount = AllocateIntoChunk(archetype, chunk, count, out allocatedIndex); entityComponentStore->AllocateEntities(archetype, chunk, allocatedIndex, allocatedCount, entities); - ReplicateComponents(src.Chunk, src.IndexInChunk, chunk, allocatedIndex, allocatedCount); + ReplicateComponents(entityComponentStore->GetArchetype(src.Chunk), src.Chunk, src.IndexInChunk, archetype, chunk, allocatedIndex, allocatedCount); // Add Entities in Chunk. ChangeVersion:Yes OrderVersion:Yes - chunk->SetAllChangeVersions(globalSystemVersion); - chunk->SetOrderVersion(globalSystemVersion); + var chunkListIndex = chunk.ListIndex; + archetype->Chunks.SetAllChangeVersion(chunkListIndex, globalSystemVersion); + archetype->Chunks.SetOrderVersion(chunkListIndex, globalSystemVersion); #if !DOTS_DISABLE_DEBUG_NAMES for (var i = 0; i < allocatedCount; ++i) entityComponentStore->CopyName(entities[i], srcEntity); #endif - entityComponentStore->ManagedChangesTracker.IncrementComponentOrderVersion(archetype, chunk->SharedComponentValues); + var sharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunkListIndex); + entityComponentStore->ManagedChangesTracker.IncrementComponentOrderVersion(archetype, sharedComponentValues); entityComponentStore->IncrementComponentTypeOrderVersion(archetype); } - public static void Deallocate(Chunk* chunk) + public static void Deallocate(Archetype* archetype, ChunkIndex chunk) { - Deallocate(new EntityBatchInChunk {Chunk = chunk, StartIndex = 0, Count = chunk->Count}); + Deallocate(archetype, new EntityBatchInChunk { Chunk = chunk, StartIndex = 0, Count = chunk.Count }); } - public static void Deallocate(in EntityBatchInChunk batch) + public static void Deallocate(Archetype* archetype, in EntityBatchInChunk batch) { var chunk = batch.Chunk; - var archetype = chunk->Archetype; var entityComponentStore = archetype->EntityComponentStore; var globalSystemVersion = entityComponentStore->GlobalSystemVersion; var startIndex = batch.StartIndex; var count = batch.Count; - entityComponentStore->DeallocateDataEntitiesInChunk(chunk, startIndex, count); - entityComponentStore->ManagedChangesTracker.IncrementComponentOrderVersion(archetype, chunk->SharedComponentValues); + entityComponentStore->DeallocateDataEntitiesInChunk(chunk, archetype, startIndex, count); + var sharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); + entityComponentStore->ManagedChangesTracker.IncrementComponentOrderVersion(archetype, sharedComponentValues); // Remove Entities in Chunk. ChangeVersion:No OrderVersion:Yes - chunk->SetOrderVersion(globalSystemVersion); + archetype->Chunks.SetOrderVersion(chunk.ListIndex, globalSystemVersion); entityComponentStore->IncrementComponentTypeOrderVersion(archetype); - chunk->Archetype->EntityCount -= count; - int newChunkEntityCount = chunk->Count - count; - SetChunkCount(chunk, newChunkEntityCount); + archetype->EntityCount -= count; + int newChunkEntityCount = chunk.Count - count; + SetChunkCount(archetype, chunk, newChunkEntityCount); // Can't update these counts until the chunk count is correct and the padding bits are clear if (newChunkEntityCount != 0) - UpdateChunkDisabledEntityCounts(chunk); + UpdateChunkDisabledEntityCounts(chunk, archetype); } - public static void Clone(in EntityBatchInChunk srcBatch, Chunk* dstChunk) + public static void Clone(Archetype* srcArchetype, in EntityBatchInChunk srcBatch, Archetype* dstArchetype, ChunkIndex dstChunk) { var srcChunk = srcBatch.Chunk; var srcChunkIndex = srcBatch.StartIndex; var srcCount = srcBatch.Count; - var dstArchetype = dstChunk->Archetype; - var srcArchetype = srcChunk->Archetype; var entityComponentStore = dstArchetype->EntityComponentStore; var globalSystemVersion = entityComponentStore->GlobalSystemVersion; // Note (srcArchetype == dstArchetype) is valid // Archetypes can the the same, but chunks still differ because filter is different (e.g. shared component) - int dstChunkIndex; - var dstValidExistingVersions = dstChunk->Count != 0; - var dstCount = AllocateIntoChunk(dstChunk, srcCount, out dstChunkIndex); + var dstValidExistingVersions = dstChunk.Count != 0; + var dstCount = AllocateIntoChunk(dstArchetype, dstChunk, srcCount, out var dstChunkIndex); Assert.IsTrue(dstCount == srcCount); - Convert(srcChunk, srcChunkIndex, dstChunk, dstChunkIndex, dstCount); + Convert(srcArchetype, srcChunk, srcChunkIndex, dstArchetype, dstChunk, dstChunkIndex, dstCount); - var dstEntities = (Entity*)ChunkDataUtility.GetComponentDataRO(dstChunk, dstChunkIndex, 0); + var dstEntities = (Entity*)ChunkDataUtility.GetComponentDataRO(dstChunk, dstArchetype, dstChunkIndex, 0); for (int i = 0; i < dstCount; i++) { var entity = dstEntities[i]; - - entityComponentStore->SetArchetype(entity, dstArchetype); entityComponentStore->SetEntityInChunk(entity, new EntityInChunk { Chunk = dstChunk, IndexInChunk = dstChunkIndex + i }); } - CloneChangeVersions(srcArchetype, srcChunk->ListIndex, dstArchetype, dstChunk->ListIndex, dstValidExistingVersions); - CloneEnabledBits(srcChunk, srcChunkIndex, dstChunk, dstChunkIndex, dstCount); + CloneChangeVersions(srcArchetype, srcChunk.ListIndex, dstArchetype, dstChunk.ListIndex, dstValidExistingVersions); + CloneEnabledBits(srcChunk, srcArchetype, srcChunkIndex, dstChunk, dstArchetype, dstChunkIndex, dstCount); // Can't update these counts until the chunk count is up to date and padding bits are clear - UpdateChunkDisabledEntityCounts(dstChunk); + UpdateChunkDisabledEntityCounts(dstChunk, dstArchetype); - dstChunk->SetOrderVersion(globalSystemVersion); + dstArchetype->Chunks.SetOrderVersion(dstChunk.ListIndex, globalSystemVersion); entityComponentStore->IncrementComponentTypeOrderVersion(dstArchetype); - entityComponentStore->ManagedChangesTracker.IncrementComponentOrderVersion(dstArchetype, dstChunk->SharedComponentValues); + var dstSharedComponentValues = dstArchetype->Chunks.GetSharedComponentValues(dstChunk.ListIndex); + entityComponentStore->ManagedChangesTracker.IncrementComponentOrderVersion(dstArchetype, dstSharedComponentValues); // Cannot DestroyEntities unless CleanupComplete on the entity chunk. - if (dstChunk->Archetype->CleanupComplete) + if (dstArchetype->CleanupComplete) entityComponentStore->DestroyEntities(dstEntities, dstCount); } - static void ReplicateComponents(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int dstBaseIndex, int count) + static void ReplicateComponents(Archetype* srcArchetype, ChunkIndex srcChunk, int srcIndex, Archetype* dstArchetype, ChunkIndex dstChunk, int dstBaseIndex, int count) { - var srcArchetype = srcChunk->Archetype; - var srcBuffer = srcChunk->Buffer; - var dstBuffer = dstChunk->Buffer; - var dstArchetype = dstChunk->Archetype; + var srcBuffer = srcChunk.Buffer; + var dstBuffer = dstChunk.Buffer; var srcOffsets = srcArchetype->Offsets; var srcSizeOfs = srcArchetype->SizeOfs; var srcBufferCapacities = srcArchetype->BufferCapacities; @@ -1391,19 +1382,17 @@ static void ReplicateComponents(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, } // Copy enabled bits from source entity to the instantiated entities - ReplicateEnabledBits(srcChunk, srcIndex, dstChunk, dstBaseIndex, count); + ReplicateEnabledBits(srcChunk, srcArchetype, srcIndex, dstChunk, dstArchetype, dstBaseIndex, count); if (dstArchetype->NumManagedComponents > 0) { - ReplicateManagedComponents(srcChunk, srcIndex, dstChunk, dstBaseIndex, count); + ReplicateManagedComponents(srcArchetype, srcChunk, srcIndex, dstArchetype, dstChunk, dstBaseIndex, count); } } - static void ReplicateManagedComponents(Chunk* srcChunk, int srcIndex, Chunk* dstChunk, int dstBaseIndex, int count) + static void ReplicateManagedComponents(Archetype* srcArchetype, ChunkIndex srcChunk, int srcIndex, Archetype* dstArchetype, ChunkIndex dstChunk, int dstBaseIndex, int count) { - var dstArchetype = dstChunk->Archetype; var entityComponentStore = dstArchetype->EntityComponentStore; - var srcArchetype = srcChunk->Archetype; var srcTypes = srcArchetype->Types; var dstTypes = dstArchetype->Types; var srcOffsets = srcArchetype->Offsets; @@ -1418,8 +1407,8 @@ static void ReplicateManagedComponents(Chunk* srcChunk, int srcIndex, Chunk* dst var firstDstManagedComponent = dstArchetype->FirstManagedComponent; var dstTypeIndex = firstDstManagedComponent; var managedComponentsEnd = srcArchetype->ManagedComponentsEnd; - var srcBaseAddr = srcChunk->Buffer + sizeof(int) * srcIndex; - var dstBaseAddr = dstChunk->Buffer + sizeof(int) * dstBaseIndex; + var srcBaseAddr = srcChunk.Buffer + sizeof(int) * srcIndex; + var dstBaseAddr = dstChunk.Buffer + sizeof(int) * dstBaseIndex; bool hasCompanionComponents = dstArchetype->HasCompanionComponents; @@ -1472,7 +1461,7 @@ static void ReplicateManagedComponents(Chunk* srcChunk, int srcIndex, Chunk* dst var companionLinkIndexInTypeArray = GetIndexInTypeArray(dstArchetype, ManagedComponentStore.CompanionLinkTypeIndex); var companionLinkIndices = (companionLinkIndexInTypeArray == -1) ? null : (int*)(dstBaseAddr + dstOffsets[companionLinkIndexInTypeArray]); - var dstEntities = (Entity*)dstChunk->Buffer + dstBaseIndex; + var dstEntities = (Entity*)dstChunk.Buffer + dstBaseIndex; entityComponentStore->ManagedChangesTracker.CloneCompanionComponentBegin(componentIndices + componentCount - nonNullCompanionComponents, nonNullCompanionComponents, dstEntities, count, companionLinkIndices); for (int c = componentCount - nonNullCompanionComponents; c < componentCount; ++c) { @@ -1483,18 +1472,14 @@ static void ReplicateManagedComponents(Chunk* srcChunk, int srcIndex, Chunk* dst } } - public static byte* GetChunkBuffer(Chunk* chunk) + public static void ClearMissingReferences(Archetype* archetype, ChunkIndex chunk) { - return chunk->Buffer; - } - - public static void ClearMissingReferences(Chunk* chunk) - { - var archetype = chunk->Archetype; var entityComponentStore = archetype->EntityComponentStore; var globalSystemVersion = entityComponentStore->GlobalSystemVersion; var typesCount = archetype->TypesCount; - var entityCount = chunk->Count; + var entityCount = chunk.Count; + var chunkBuffer = chunk.Buffer; + var listIndex = chunk.ListIndex; for (var typeIndexInArchetype = 1; typeIndexInArchetype < typesCount; typeIndexInArchetype++) { @@ -1507,7 +1492,7 @@ public static void ClearMissingReferences(Chunk* chunk) } ref readonly var typeInfo = ref entityComponentStore->GetTypeInfo(componentTypeInArchetype.TypeIndex); - var typeInChunkPtr = GetChunkBuffer(chunk) + archetype->Offsets[typeIndexInArchetype]; + var typeInChunkPtr = chunkBuffer + archetype->Offsets[typeIndexInArchetype]; var typeSizeOf = archetype->SizeOfs[typeIndexInArchetype]; var changed = false; @@ -1534,7 +1519,7 @@ public static void ClearMissingReferences(Chunk* chunk) if (changed) { - chunk->SetChangeVersion(typeIndexInArchetype, globalSystemVersion); + archetype->Chunks.SetChangeVersion(typeIndexInArchetype, listIndex, globalSystemVersion); } } } @@ -1564,25 +1549,22 @@ static bool ClearEntityReferences(EntityComponentStore* entityComponentStore, in return changed; } - public static Entity GetEntityFromEntityInChunk(EntityInChunk entityInChunk) + public static Entity GetEntityFromEntityInChunk(Archetype* archetype, EntityInChunk entityInChunk) { - var chunk = entityInChunk.Chunk; - var archetype = chunk->Archetype; - var buffer = GetChunkBuffer(chunk) + archetype->Offsets[0] + entityInChunk.IndexInChunk * archetype->SizeOfs[0]; + var buffer = entityInChunk.Chunk.Buffer + archetype->Offsets[0] + entityInChunk.IndexInChunk * archetype->SizeOfs[0]; return *(Entity*)buffer; } - public static void AddExistingChunk(Chunk* chunk, int* sharedComponentIndices, byte* enabledBitsValuesForChunk, int* perComponentDisabledBitCount) + public static void AddExistingChunk(Archetype* archetype, ChunkIndex chunk, int* sharedComponentIndices, byte* enabledBitsValuesForChunk, int* perComponentDisabledBitCount) { - var archetype = chunk->Archetype; var entityComponentStore = archetype->EntityComponentStore; var globalSystemVersion = entityComponentStore->GlobalSystemVersion; archetype->AddToChunkList(chunk, sharedComponentIndices, globalSystemVersion, ref entityComponentStore->m_ChunkListChangesTracker); - archetype->EntityCount += chunk->Count; + archetype->EntityCount += chunk.Count; - InitializeBitsForNewChunk(archetype, chunk->ListIndex); - Assert.IsTrue(chunk->ListIndex >= 0 && chunk->ListIndex < archetype->Chunks.Count); - archetype->Chunks.SetEnabledBitsAndHierarchicalData(chunk->ListIndex, enabledBitsValuesForChunk, perComponentDisabledBitCount); + InitializeBitsForNewChunk(archetype, chunk.ListIndex); + Assert.IsTrue(chunk.ListIndex >= 0 && chunk.ListIndex < archetype->Chunks.Count); + archetype->Chunks.SetEnabledBitsAndHierarchicalData(chunk.ListIndex, enabledBitsValuesForChunk, perComponentDisabledBitCount); for (var i = 0; i < archetype->NumSharedComponents; ++i) { @@ -1593,31 +1575,29 @@ public static void AddExistingChunk(Chunk* chunk, int* sharedComponentIndices, b } else { entityComponentStore->ManagedChangesTracker.AddReference(sharedComponentIndex); } - } - if (chunk->Count < chunk->Capacity) + if (chunk.Count < archetype->ChunkCapacity) archetype->EmptySlotTrackingAddChunk(chunk); entityComponentStore->AddExistingEntitiesInChunk(chunk); } - public static void AddEmptyChunk(Archetype* archetype, Chunk* chunk, SharedComponentValues sharedComponentValues) + public static void AddEmptyChunk(Archetype* archetype, ChunkIndex chunk, SharedComponentValues sharedComponentValues) { var entityComponentStore = archetype->EntityComponentStore; var globalSystemVersion = entityComponentStore->GlobalSystemVersion; - chunk->Archetype = archetype; - chunk->Count = 0; - chunk->Capacity = archetype->ChunkCapacity; - chunk->SequenceNumber = entityComponentStore->AllocateSequenceNumber(); - chunk->metaChunkEntity = Entity.Null; + entityComponentStore->SetArchetype(chunk, archetype); + chunk.Count = 0; + chunk.SequenceNumber = entityComponentStore->AllocateSequenceNumber(); + chunk.MetaChunkEntity = Entity.Null; var numSharedComponents = archetype->NumSharedComponents; if (numSharedComponents > 0) { - for (var i = 0; i < archetype->NumSharedComponents; ++i) + for (var i = 0; i < numSharedComponents; ++i) { var sharedComponentIndex = sharedComponentValues[i]; if (EntityComponentStore.IsUnmanagedSharedComponentIndex(sharedComponentIndex)) @@ -1631,7 +1611,7 @@ public static void AddEmptyChunk(Archetype* archetype, Chunk* chunk, SharedCompo archetype->AddToChunkList(chunk, sharedComponentValues, globalSystemVersion, ref entityComponentStore->m_ChunkListChangesTracker); - InitializeBitsForNewChunk(archetype, chunk->ListIndex); + InitializeBitsForNewChunk(archetype, chunk.ListIndex); Assert.IsTrue(archetype->Chunks.Count != 0); @@ -1644,11 +1624,11 @@ public static void AddEmptyChunk(Archetype* archetype, Chunk* chunk, SharedCompo } else { - Assert.IsTrue(archetype->FreeChunksBySharedComponents.TryGet(chunk->SharedComponentValues, + Assert.IsTrue(archetype->FreeChunksBySharedComponents.TryGet(archetype->Chunks.GetSharedComponentValues(chunk.ListIndex), archetype->NumSharedComponents) != null); } - chunk->Flags = 0; + chunk.Flags = 0; } } } diff --git a/Unity.Entities/ChunkListMap.cs b/Unity.Entities/ChunkListMap.cs index 6f7a917..33083d7 100644 --- a/Unity.Entities/ChunkListMap.cs +++ b/Unity.Entities/ChunkListMap.cs @@ -36,7 +36,8 @@ static uint GetHashCode(SharedComponentValues sharedComponentValues, int numShar } private UnsafeList hashes; - private UnsafePtrList chunks; + private UnsafeList chunks; + private Archetype* archetype; private int emptyNodes; private int skipNodes; @@ -79,8 +80,9 @@ public void SetCapacity(int capacity) hashes.SetCapacity(capacity); } - public void Init(int count) + public void Init(Archetype* archetype, int count) { + this.archetype = archetype; if (count < MinimumSize) count = MinimumSize; Assert.IsTrue(0 == (count & (count - 1))); @@ -94,7 +96,7 @@ public void Init(int count) if (chunks.IsCreated) chunks.Clear(); else - chunks = new UnsafePtrList(count, Allocator.Persistent); + chunks = new UnsafeList(count, Allocator.Persistent); chunks.Resize(count, NativeArrayOptions.ClearMemory); emptyNodes = count; @@ -111,7 +113,7 @@ public void AppendFrom(ref ChunkListMap src) } } - public Chunk* TryGet(SharedComponentValues sharedComponentValues, int numSharedComponents) + public ChunkIndex TryGet(SharedComponentValues sharedComponentValues, int numSharedComponents) { uint desiredHash = GetHashCode(sharedComponentValues, numSharedComponents); int offset = (int)(desiredHash & (uint)hashMask); @@ -120,17 +122,18 @@ public void AppendFrom(ref ChunkListMap src) { var hash = hashes.Ptr[offset]; if (hash == 0) - return null; + return ChunkIndex.Null; if (hash == desiredHash) { var chunk = chunks.Ptr[offset]; - if (sharedComponentValues.EqualTo(chunk->SharedComponentValues, numSharedComponents)) + var sharedComponentValuesFromChunk = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); + if (sharedComponentValues.EqualTo(sharedComponentValuesFromChunk, numSharedComponents)) return chunk; } offset = (offset + 1) & hashMask; ++attempts; if (attempts == Size) - return null; + return ChunkIndex.Null; } } @@ -142,7 +145,7 @@ public void Resize(int size) return; var temp = this; this = new ChunkListMap(); - Init(size); + Init(temp.archetype, size); AppendFrom(ref temp); temp.Dispose(); } @@ -159,12 +162,11 @@ public void PossiblyShrink() Resize(Size / 2); } - public void Add(Chunk* chunk) + public void Add(ChunkIndex chunk) { - Assert.IsTrue(chunk != null); - Assert.IsTrue(chunk->Archetype != null); - var sharedComponentValues = chunk->SharedComponentValues; - int numSharedComponents = chunk->Archetype->NumSharedComponents; + Assert.IsTrue(chunk != ChunkIndex.Null); + var sharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); + int numSharedComponents = archetype->NumSharedComponents; uint desiredHash = GetHashCode(sharedComponentValues, numSharedComponents); int offset = (int)(desiredHash & (uint)hashMask); int attempts = 0; @@ -175,7 +177,7 @@ public void Add(Chunk* chunk) { hashes.Ptr[offset] = desiredHash; chunks.Ptr[offset] = chunk; - chunk->ListWithEmptySlotsIndex = offset; + chunk.ListWithEmptySlotsIndex = offset; --emptyNodes; PossiblyGrow(); return; @@ -185,7 +187,7 @@ public void Add(Chunk* chunk) { hashes.Ptr[offset] = desiredHash; chunks.Ptr[offset] = chunk; - chunk->ListWithEmptySlotsIndex = offset; + chunk.ListWithEmptySlotsIndex = offset; --skipNodes; PossiblyGrow(); return; @@ -197,20 +199,20 @@ public void Add(Chunk* chunk) } } - public void Remove(Chunk* chunk) + public void Remove(ChunkIndex chunk) { - int offset = chunk->ListWithEmptySlotsIndex; - chunk->ListWithEmptySlotsIndex = -1; + int offset = chunk.ListWithEmptySlotsIndex; + chunk.ListWithEmptySlotsIndex = -1; Assert.IsTrue(offset != -1); - Assert.IsTrue(chunks.Ptr[offset] == chunk); + Assert.IsTrue(chunks[offset] == chunk); hashes.Ptr[offset] = kSkipCode; ++skipNodes; PossiblyShrink(); } - public bool Contains(Chunk* chunk) + public bool Contains(ChunkIndex chunk) { - var offset = chunk->ListWithEmptySlotsIndex; + var offset = chunk.ListWithEmptySlotsIndex; return offset != -1 && chunks.Ptr[offset] == chunk; } diff --git a/Unity.Entities/ComponentSafetyHandles.cs b/Unity.Entities/ComponentSafetyHandles.cs index 14fd111..5c9b3af 100644 --- a/Unity.Entities/ComponentSafetyHandles.cs +++ b/Unity.Entities/ComponentSafetyHandles.cs @@ -180,7 +180,7 @@ public static void Initialize() m_StaticSafetyIdData.Data.m_StaticSafetyIdsForArchetypeChunkArrays = (int*)Memory.Unmanaged.Allocate(sizeof(int) * kMaxTypes, 16, Allocator.Persistent); UnsafeUtility.MemClear(m_StaticSafetyIdData.Data.m_StaticSafetyIdsForArchetypeChunkArrays, sizeof(int) * kMaxTypes); -#if !UNITY_DOTSRUNTIME + if (!s_AppDomainUnloadRegistered) { // important: this will always be called from a special unload thread (main thread will be blocking on this) @@ -190,7 +190,6 @@ public static void Initialize() System.AppDomain.CurrentDomain.ProcessExit += (_, __) => { Shutdown(); }; s_AppDomainUnloadRegistered = true; } -#endif } static void Shutdown() diff --git a/Unity.Entities/ComponentSystemBase.cs b/Unity.Entities/ComponentSystemBase.cs index fc2a891..9e11882 100644 --- a/Unity.Entities/ComponentSystemBase.cs +++ b/Unity.Entities/ComponentSystemBase.cs @@ -4,6 +4,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using Unity.Burst; +using Unity.Burst.CompilerServices; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Core; @@ -32,10 +33,8 @@ public ComponentSystemBase() [DebuggerBrowsable(DebuggerBrowsableState.Never)] internal SystemState* m_StatePtr; -#if !NET_DOTS // This property exists so that the SystemSate is visible in the .NET Debugger. SystemState_ SystemState => SystemState_.FromPointer(m_StatePtr); -#endif internal SystemState* CheckedState() { @@ -738,7 +737,7 @@ public bool HasSingleton() { #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG var typeIndex = TypeManager.GetTypeIndex(); - if (TypeManager.IsEnableable(typeIndex)) + if (Hint.Unlikely(TypeManager.IsEnableable(typeIndex))) { var typeName = typeIndex.ToFixedString(); throw new InvalidOperationException( @@ -747,7 +746,16 @@ public bool HasSingleton() #endif var type = ComponentType.ReadOnly(); var query = CheckedState()->GetSingletonEntityQueryInternal(type); - return query.CalculateEntityCount() == 1; + int matchingEntityCount = query.CalculateEntityCount(); +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(matchingEntityCount > 1)) + { + var typeName = typeIndex.ToFixedString(); + throw new InvalidOperationException( + $"HasSingleton<{typeName}>() found {matchingEntityCount} instances of {typeName}; there must only be either zero or one."); + } +#endif + return matchingEntityCount == 1; } /// @@ -831,10 +839,17 @@ public bool TryGetSingleton(out T value) var type = ComponentType.ReadOnly(); var query = CheckedState()->GetSingletonEntityQueryInternal(type); - var hasSingleton = query.CalculateEntityCount() == 1; - + int matchingEntityCount = query.CalculateEntityCount(); +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(matchingEntityCount > 1)) + { + var typeName = type.TypeIndex.ToFixedString(); + throw new InvalidOperationException( + $"TryGetSingleton<{typeName}>() found {matchingEntityCount} instances of {typeName}; there must only be either zero or one."); + } +#endif + bool hasSingleton = matchingEntityCount == 1; value = hasSingleton ? query.GetSingleton() : default; - return hasSingleton; } @@ -852,10 +867,17 @@ public bool TryGetSingletonBuffer(out DynamicBuffer value) var type = ComponentType.ReadOnly(); var query = CheckedState()->GetSingletonEntityQueryInternal(type); - var hasSingleton = query.CalculateEntityCount() == 1; - + int matchingEntityCount = query.CalculateEntityCount(); +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(matchingEntityCount > 1)) + { + var typeName = type.TypeIndex.ToFixedString(); + throw new InvalidOperationException( + $"TryGetSingletonBuffer<{typeName}>() found {matchingEntityCount} instances of {typeName}; there must only be either zero or one."); + } +#endif + bool hasSingleton = matchingEntityCount == 1; value = hasSingleton ? GetSingletonBuffer() : default; - return hasSingleton; } @@ -906,10 +928,17 @@ public bool TryGetSingletonEntity(out Entity value) { var type = ComponentType.ReadOnly(); var query = CheckedState()->GetSingletonEntityQueryInternal(type); - var hasSingleton = query.CalculateEntityCount() == 1; - + int matchingEntityCount = query.CalculateEntityCount(); +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(matchingEntityCount > 1)) + { + var typeName = type.TypeIndex.ToFixedString(); + throw new InvalidOperationException( + $"TryGetSingletonEntity<{typeName}>() found {matchingEntityCount} instances of {typeName}; there must only be either zero or one."); + } +#endif + bool hasSingleton = matchingEntityCount == 1; value = hasSingleton ? query.GetSingletonEntity() : Entity.Null; - return hasSingleton; } diff --git a/Unity.Entities/ComponentSystemGroup.cs b/Unity.Entities/ComponentSystemGroup.cs index e47211b..9eefd5d 100644 --- a/Unity.Entities/ComponentSystemGroup.cs +++ b/Unity.Entities/ComponentSystemGroup.cs @@ -92,9 +92,10 @@ protected set internal UnsafeList m_UnmanagedSystemsToRemove; /// - /// The ordered list of managed systems in this group, sorted by update order. + /// The list of managed systems in this group, sorted by update order. /// public virtual IReadOnlyList ManagedSystems => m_managedSystemsToUpdate; + internal UnsafeList UnmanagedSystems => m_UnmanagedSystemsToUpdate; /// /// Get the list of unmanaged systems in this group, sorted by update order. @@ -115,7 +116,7 @@ public NativeList GetUnmanagedSystems(Allocator allocator = Alloca /// A NativeList of systems public NativeList GetAllSystems(Allocator allocator = Allocator.Temp) { - var ret = new NativeList(m_MasterUpdateList.Length, allocator); + var ret = new NativeList(16, allocator); for (int i = 0; i < m_MasterUpdateList.Length; i++) { var entry = m_MasterUpdateList[i]; @@ -123,7 +124,7 @@ public NativeList GetAllSystems(Allocator allocator = Allocator.Te } return ret; } - + internal DoubleRewindableAllocators* m_RateGroupAllocators = null; internal byte RateGroupAllocatorsCreated { get; set; } = 0; @@ -591,23 +592,6 @@ public void SortSystems() RecurseUpdate(); } -#if UNITY_DOTSRUNTIME - public void RecursiveLogToConsole() - { - foreach (var sys in m_managedSystemsToUpdate) - { - if (sys is ComponentSystemGroup) - { - (sys as ComponentSystemGroup).RecursiveLogToConsole(); - } - - var name = TypeManager.GetSystemName(sys.GetType()); - Debug.Log(name); - } - } - -#endif - internal override void OnStopRunningInternal() { OnStopRunning(); @@ -742,13 +726,6 @@ void UpdateAllSystems() catch (Exception e) { Debug.LogException(e); -#if UNITY_DOTSRUNTIME - // When in a DOTS Runtime build, throw this upstream -- continuing after silently eating an exception - // is not what you'll want, except maybe once we have LiveLink. If you're looking at this code - // because your LiveLink dots runtime build is exiting when you don't want it to, feel free - // to remove this block, or guard it with something to indicate the player is not for live link. - throw; -#endif } if (World.QuitUpdate) diff --git a/Unity.Entities/ComponentSystemSorter.cs b/Unity.Entities/ComponentSystemSorter.cs index a0dadf9..d4baa0f 100644 --- a/Unity.Entities/ComponentSystemSorter.cs +++ b/Unity.Entities/ComponentSystemSorter.cs @@ -1,13 +1,12 @@ using System; using System.Collections.Generic; -#if !NET_DOTS using System.Linq; -#endif using Unity; using Unity.Burst; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Entities; +using UnityEngine.Serialization; namespace Unity.Entities { @@ -144,7 +143,7 @@ internal static unsafe int LookupSystemElement(SystemTypeIndex typeIndex, Native internal struct SystemElement { - public SystemTypeIndex SystemTypeIndex; + public int SystemTypeIndex; public UpdateIndex Index; public int OrderingBucket; // 0 = OrderFirst, 1 = none, 2 = OrderLast public NativeList updateBefore; @@ -198,7 +197,7 @@ internal static unsafe void SortInternal( var sortedElements = new UnsafeList(elements.Length, Allocator.Temp); sortedElements.Length = elements.Length; - var sortedIndices = new UnsafeList(elements.Length, + var sortedIndices = new UnsafeList(elements.Length, Allocator.Temp); sortedIndices.Length = elements.Length; @@ -484,7 +483,8 @@ internal static unsafe void WarnAboutAnySystemAttributeBadness(int systemTypeInd for (int i = 0; i < group.m_UnmanagedSystemsToUpdate.Length; i++) { if (TypeManager.GetSystemType( - group.World.Unmanaged.ResolveSystemState(group.m_UnmanagedSystemsToUpdate[i])-> + group.World.Unmanaged.ResolveSystemState( + group.m_UnmanagedSystemsToUpdate[i])-> m_SystemTypeIndex) == targetType) { @@ -503,10 +503,7 @@ internal static unsafe void WarnAboutAnySystemAttributeBadness(int systemTypeInd continue; } } - } - if (group != null) - { var groupTypeIndex = TypeManager.GetSystemTypeIndex(groupType); var thisBucket = ComponentSystemGroup.ComputeSystemOrdering(systemTypeIndex, groupTypeIndex); @@ -545,7 +542,7 @@ internal static unsafe void FindConstraints( var before = TypeManager.GetSystemAttributes(systemTypeIndex, beforekind); var after = TypeManager.GetSystemAttributes(systemTypeIndex, afterkind); - + for (int j = 0; j < before.Length; j++) { var attr = before[j]; @@ -567,7 +564,7 @@ internal static unsafe void FindConstraints( sysElems.ElementAt(i).updateBefore.Add(attr.TargetSystemTypeIndex); sysElems.ElementAt(depIndex).nAfter++; } - + for (int j = 0; j < after.Length; j++) { var attr = after[j]; diff --git a/Unity.Entities/Content/Delivery/ContentDeliveryGlobalState.cs b/Unity.Entities/Content/Delivery/ContentDeliveryGlobalState.cs index abe07fd..ce991cc 100644 --- a/Unity.Entities/Content/Delivery/ContentDeliveryGlobalState.cs +++ b/Unity.Entities/Content/Delivery/ContentDeliveryGlobalState.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.IO; using Unity.Collections; @@ -302,4 +301,3 @@ internal ContentUpdateState AttemptToSetContentPathsToStreamingAssets() } } -#endif diff --git a/Unity.Entities/Content/Delivery/ContentDeliveryService.cs b/Unity.Entities/Content/Delivery/ContentDeliveryService.cs index bf9a862..f85d25e 100644 --- a/Unity.Entities/Content/Delivery/ContentDeliveryService.cs +++ b/Unity.Entities/Content/Delivery/ContentDeliveryService.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Collections.Generic; using System.IO; @@ -638,4 +637,3 @@ public void Dispose() } } } -#endif diff --git a/Unity.Entities/Content/Delivery/ContentDownloadService.cs b/Unity.Entities/Content/Delivery/ContentDownloadService.cs index b8c487c..e03ec2c 100644 --- a/Unity.Entities/Content/Delivery/ContentDownloadService.cs +++ b/Unity.Entities/Content/Delivery/ContentDownloadService.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Collections.Generic; using System.IO; @@ -525,4 +524,3 @@ protected override bool ProcessDownload(ref long downloadedBytes, ref string err } } } -#endif diff --git a/Unity.Entities/Content/Delivery/ContentLocationService.cs b/Unity.Entities/Content/Delivery/ContentLocationService.cs index 22e628c..4654a44 100644 --- a/Unity.Entities/Content/Delivery/ContentLocationService.cs +++ b/Unity.Entities/Content/Delivery/ContentLocationService.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Collections.Generic; using Unity.Collections; @@ -137,4 +136,3 @@ public struct RemoteContentSetData } } -#endif diff --git a/Unity.Entities/Content/Delivery/DefaultLocationService.cs b/Unity.Entities/Content/Delivery/DefaultLocationService.cs index 21b6acc..37bd0fe 100644 --- a/Unity.Entities/Content/Delivery/DefaultLocationService.cs +++ b/Unity.Entities/Content/Delivery/DefaultLocationService.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Collections.Generic; using Unity.Collections; @@ -132,4 +131,3 @@ public override bool GetResolvedRemoteContentLocations(ref NativeHashSet Hash.GetHashCode(); } } -#endif diff --git a/Unity.Entities/Content/Delivery/RemoteContentLocation.cs b/Unity.Entities/Content/Delivery/RemoteContentLocation.cs index 6190b9b..7afd0e0 100644 --- a/Unity.Entities/Content/Delivery/RemoteContentLocation.cs +++ b/Unity.Entities/Content/Delivery/RemoteContentLocation.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using Unity.Collections; @@ -71,4 +70,3 @@ public override int GetHashCode() } } } -#endif diff --git a/Unity.Entities/Content/IRuntimeCatalogDataSource.cs b/Unity.Entities/Content/IRuntimeCatalogDataSource.cs index 41340fe..f7f5886 100644 --- a/Unity.Entities/Content/IRuntimeCatalogDataSource.cs +++ b/Unity.Entities/Content/IRuntimeCatalogDataSource.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System.Collections.Generic; using Unity.Entities.Serialization; @@ -40,4 +39,3 @@ internal interface IRuntimeCatalogDataSource IEnumerable<(UntypedWeakReferenceId, string)> GetScenes(ContentFileId fileId); } } -#endif diff --git a/Unity.Entities/Content/RuntimeContentCatalog.cs b/Unity.Entities/Content/RuntimeContentCatalog.cs index c05727a..0f6bd87 100644 --- a/Unity.Entities/Content/RuntimeContentCatalog.cs +++ b/Unity.Entities/Content/RuntimeContentCatalog.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.IO; using System.Runtime.InteropServices; @@ -516,5 +515,3 @@ public string this[int i] } } - -#endif diff --git a/Unity.Entities/Content/RuntimeContentCatalogData.cs b/Unity.Entities/Content/RuntimeContentCatalogData.cs index c914381..d1ad905 100644 --- a/Unity.Entities/Content/RuntimeContentCatalogData.cs +++ b/Unity.Entities/Content/RuntimeContentCatalogData.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Runtime.InteropServices; using Unity.Collections; @@ -207,4 +206,3 @@ internal struct RuntimeContentCatalogData public BlobArray Blobs; } } -#endif diff --git a/Unity.Entities/Content/RuntimeContentCatalogDataUtility.cs b/Unity.Entities/Content/RuntimeContentCatalogDataUtility.cs index 523fce2..355ccf9 100644 --- a/Unity.Entities/Content/RuntimeContentCatalogDataUtility.cs +++ b/Unity.Entities/Content/RuntimeContentCatalogDataUtility.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Collections.Generic; using System.Linq; @@ -125,4 +124,3 @@ public static void Create(IRuntimeCatalogDataSource dataSource, BlobBuilder blob } } } -#endif diff --git a/Unity.Entities/Content/RuntimeContentManager.cs b/Unity.Entities/Content/RuntimeContentManager.cs index b55e348..654bcd0 100644 --- a/Unity.Entities/Content/RuntimeContentManager.cs +++ b/Unity.Entities/Content/RuntimeContentManager.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME //#define ENABLE_CONTENT_DIAGNOSTICS using System; using UnityEngine; @@ -1305,4 +1304,3 @@ internal interface IAlternativeLoader : IDisposable } } -#endif diff --git a/Unity.Entities/Content/RuntimeContentManagerProfiler.cs b/Unity.Entities/Content/RuntimeContentManagerProfiler.cs index 39c356f..4bb3789 100644 --- a/Unity.Entities/Content/RuntimeContentManagerProfiler.cs +++ b/Unity.Entities/Content/RuntimeContentManagerProfiler.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME #define ENABLE_PROFILER #if ENABLE_PROFILER using System; @@ -207,4 +206,3 @@ public static void RecordUnloadArchive() } } #endif -#endif diff --git a/Unity.Entities/Content/RuntimeContentManagerUtility.cs b/Unity.Entities/Content/RuntimeContentManagerUtility.cs index e13be57..95d0813 100644 --- a/Unity.Entities/Content/RuntimeContentManagerUtility.cs +++ b/Unity.Entities/Content/RuntimeContentManagerUtility.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using Unity.Collections; using Unity.Entities.Serialization; @@ -224,4 +223,3 @@ public void Dispose() } } } -#endif diff --git a/Unity.Entities/Content/RuntimeContentSystem.cs b/Unity.Entities/Content/RuntimeContentSystem.cs index dae82f5..7ca0bcf 100644 --- a/Unity.Entities/Content/RuntimeContentSystem.cs +++ b/Unity.Entities/Content/RuntimeContentSystem.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using Unity.Burst; using Unity.Collections; @@ -172,4 +171,3 @@ static bool TryGetAppArg(string name, ref string value) } } } -#endif diff --git a/Unity.Entities/Content/WeakObjectReference.cs b/Unity.Entities/Content/WeakObjectReference.cs index df531ad..7843053 100644 --- a/Unity.Entities/Content/WeakObjectReference.cs +++ b/Unity.Entities/Content/WeakObjectReference.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Runtime.InteropServices; using Unity.Collections; @@ -59,11 +58,10 @@ public WeakObjectReference(TObject unityObject) this.Id = UntypedWeakReferenceId.CreateFromObjectInstance(unityObject); } #endif - /// - /// Construct a WeakObjectReference with an existing WeakObjectReference id . + /// Weak reference to an object. /// - /// Existing id for some weakly referenced data. + /// The UntypedWeakReferenceId of the object. public WeakObjectReference(UntypedWeakReferenceId id) { this.Id = id; @@ -118,4 +116,3 @@ public override int GetHashCode() } } } -#endif diff --git a/Unity.Entities/Content/WeakObjectSceneReference.cs b/Unity.Entities/Content/WeakObjectSceneReference.cs index eeba70b..def158f 100644 --- a/Unity.Entities/Content/WeakObjectSceneReference.cs +++ b/Unity.Entities/Content/WeakObjectSceneReference.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using Unity.Entities.Serialization; using UnityEngine.SceneManagement; @@ -104,5 +103,3 @@ public override int GetHashCode() public void Release() { } } } -#endif - diff --git a/Unity.Entities/DebugView.cs b/Unity.Entities/DebugView.cs index 31c0270..e834ceb 100644 --- a/Unity.Entities/DebugView.cs +++ b/Unity.Entities/DebugView.cs @@ -11,7 +11,6 @@ namespace Unity.Entities { sealed class ArchetypeChunkDataDebugView { -#if !NET_DOTS private ArchetypeChunkData m_ChunkData; public ArchetypeChunkDataDebugView(ArchetypeChunkData chunkData) @@ -29,12 +28,10 @@ public unsafe ArchetypeChunk[] Items return result; } } -#endif } sealed class UnsafeMatchingArchetypePtrListDebugView { -#if !NET_DOTS private UnsafeMatchingArchetypePtrList m_MatchingArchetypeList; public UnsafeMatchingArchetypePtrListDebugView(UnsafeMatchingArchetypePtrList MatchingArchetypeList) @@ -52,7 +49,6 @@ public unsafe MatchingArchetype*[] Items return result; } } -#endif } internal class Component_E @@ -82,7 +78,6 @@ public override string ToString() sealed class EntityManagerDebugView { -#if !NET_DOTS private EntityManager m_target; public EntityManagerDebugView(EntityManager target) { @@ -157,12 +152,10 @@ public string NumEntityNames } } public World World => m_target.World; -#endif //!NET_DOTS } sealed class WorldDebugView { -#if !NET_DOTS private World m_world; public WorldDebugView(World world) { @@ -186,18 +179,16 @@ public List AllSystems public ComponentSystemBase SimulationSystemGroup => m_world.GetExistingSystemManaged(); public ComponentSystemBase PresentationSystemGroup => m_world.GetExistingSystemManaged(); public EntityManager EntityManager => m_world.EntityManager; -#endif //#!NET_DOTS } sealed class ArchetypeChunkDebugView { -#if !NET_DOTS private ArchetypeChunk m_ArchetypeChunk; public ArchetypeChunkDebugView(ArchetypeChunk ArchetypeChunk) { m_ArchetypeChunk = ArchetypeChunk; } - public unsafe EntityArchetype Archetype => new EntityArchetype(m_ArchetypeChunk.m_Chunk->Archetype); + public unsafe EntityArchetype Archetype => new EntityArchetype(m_ArchetypeChunk.Archetype.Archetype); public unsafe Entity_[] Entities @@ -205,13 +196,13 @@ public unsafe Entity_[] Entities get { var chunk = m_ArchetypeChunk.m_Chunk; - if (chunk == null) + if (chunk == ChunkIndex.Null) return null; var length = m_ArchetypeChunk.Count; var result = new Entity_[length]; - var entityPtr = (Entity*) ChunkIterationUtility.GetChunkComponentDataROPtr(chunk, 0); + var entityPtr = (Entity*) ChunkIterationUtility.GetChunkComponentDataROPtr(m_ArchetypeChunk.Archetype.Archetype, chunk, 0); for (int i = 0; i < length; i++) result[i] = new Entity_(m_ArchetypeChunk.m_EntityComponentStore, entityPtr[i], false); @@ -224,19 +215,20 @@ public unsafe object[] SharedComponents get { var chunk = m_ArchetypeChunk.m_Chunk; - if (chunk == null) - return new object[0]; + if (chunk == ChunkIndex.Null) + return Array.Empty(); - var archetype = chunk->Archetype; + var archetype = m_ArchetypeChunk.Archetype.Archetype; object[] result = new object[archetype->NumSharedComponents]; - var types = chunk->Archetype->TypesCount; + var types = archetype->TypesCount; int sharedIter = 0; for (var i = 0; i < types; ++i) { - var componentType = chunk->Archetype->Types[i]; + var componentType = archetype->Types[i]; if (componentType.IsSharedComponent) { - result[sharedIter] = new DebuggerDataAccess(m_ArchetypeChunk.m_EntityComponentStore).GetSharedComponentDataBoxed(chunk->SharedComponentValues[sharedIter], componentType.TypeIndex); + var sharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); + result[sharedIter] = new DebuggerDataAccess(m_ArchetypeChunk.m_EntityComponentStore).GetSharedComponentDataBoxed(sharedComponentValues[sharedIter], componentType.TypeIndex); sharedIter++; } } @@ -249,10 +241,10 @@ public unsafe Entity_ ChunkComponent get { var chunk = m_ArchetypeChunk.m_Chunk; - if (chunk == null) + if (chunk == ChunkIndex.Null) return Entity_.Null; - return new Entity_(m_ArchetypeChunk.m_EntityComponentStore, m_ArchetypeChunk.m_Chunk->metaChunkEntity, false); + return new Entity_(m_ArchetypeChunk.m_EntityComponentStore, m_ArchetypeChunk.m_Chunk.MetaChunkEntity, false); } } @@ -262,20 +254,19 @@ public unsafe ComponentType_[] ComponentTypes get { var chunk = m_ArchetypeChunk.m_Chunk; - if (chunk == null) + if (chunk == ChunkIndex.Null) return null; - var archetype = chunk->Archetype; + var archetype = m_ArchetypeChunk.Archetype.Archetype; ComponentType_[] result = new ComponentType_[archetype->TypesCount]; for (var i = 0; i < archetype->TypesCount; ++i) { int memoryOrderIndexInArchetype = archetype->TypeIndexInArchetypeToMemoryOrderIndex[i]; var componentType = archetype->Types[i].ToComponentType(); result[i].Type = componentType; - result[i].Version = chunk->GetChangeVersion(i); + result[i].Version = archetype->Chunks.GetChangeVersion(i, chunk.ListIndex); result[i].IsEnableable = TypeManager.IsEnableable(componentType.TypeIndex); - result[i].NumDisabledEntitiesInChunk = - chunk->Archetype->Chunks.GetChunkDisabledCountForType(memoryOrderIndexInArchetype, chunk->ListIndex); + result[i].NumDisabledEntitiesInChunk = archetype->Chunks.GetChunkDisabledCountForType(memoryOrderIndexInArchetype, chunk.ListIndex); } return result; @@ -287,13 +278,12 @@ public unsafe uint OrderVersion get { var chunk = m_ArchetypeChunk.m_Chunk; - if (chunk == null) + if (chunk == ChunkIndex.Null) return 0; - return chunk->GetOrderVersion(); + return m_ArchetypeChunk.Archetype.Archetype->Chunks.GetOrderVersion(chunk.ListIndex); } } -#endif //!NET_DOTS } struct ComponentType_ @@ -314,7 +304,6 @@ public override string ToString() sealed class ComponentTypeSetDebugView { -#if !NET_DOTS private ComponentTypeSet m_ComponentTypeSet; public ComponentTypeSetDebugView(in ComponentTypeSet componentTypeSet) { @@ -332,13 +321,10 @@ public unsafe ComponentType[] ComponentTypes return result; } } - -#endif //!NET_DOTS } sealed class EntityArchetypeDebugView { -#if !NET_DOTS private EntityArchetype m_EntityArchetype; public EntityArchetypeDebugView(EntityArchetype entityArchetype) { @@ -387,15 +373,17 @@ public unsafe ChunkReport ChunkUtilization if (numChunks <= 0) return default; + var chunkCapacity = (float)archetype->ChunkCapacity; + var result = new ChunkReport { PerChunk = new List(numChunks), - WorstUtilization = archetype->Chunks[0]->Count / (float) archetype->Chunks[0]->Capacity + WorstUtilization = archetype->Chunks[0].Count / chunkCapacity }; for (var i = 0; i < numChunks; ++i) { - var avg = archetype->Chunks[i]->Count / (float)archetype->Chunks[i]->Capacity; + var avg = archetype->Chunks[i].Count / chunkCapacity; result.PerChunk.Add(avg); result.AvgUtilization += avg; if (avg < result.WorstUtilization) @@ -485,13 +473,10 @@ public unsafe Dictionary TypeMemoryOrderIndex return result; } } - -#endif //!NET_DOTS } sealed class EntityQueryDebugView { -#if !NET_DOTS private EntityQuery m_EntityQuery; public EntityQueryDesc Desc @@ -570,13 +555,10 @@ public EntityQueryDebugView(EntityQuery query) { m_EntityQuery = query; } - -#endif //!NET_DOTS } class SystemDebugView { -#if !NET_DOTS [DebuggerBrowsable(DebuggerBrowsableState.Never)] readonly World m_World; [DebuggerBrowsable(DebuggerBrowsableState.Never)] @@ -668,12 +650,10 @@ public override string ToString() return "System has been disposed"; return type.Name; } -#endif //!NET_DOTS } sealed class ComponentSystemGroupDebugView { -#if !NET_DOTS private ComponentSystemGroup m_componentSystemGroup; public ComponentSystemGroupDebugView(ComponentSystemGroup mComponentSystemGroup) @@ -708,12 +688,10 @@ public SystemDebugView[] Systems public bool Enabled => m_componentSystemGroup.Enabled; public bool EnableSystemSorting => m_componentSystemGroup.EnableSystemSorting; public World World => m_componentSystemGroup.World; -#endif //!NET_DOTS } sealed unsafe class SystemState_ { -#if !NET_DOTS [DebuggerBrowsable(DebuggerBrowsableState.Never)] SystemHandle _SystemHandle; [DebuggerBrowsable(DebuggerBrowsableState.Never)] @@ -810,8 +788,5 @@ public override string ToString() { return GetName(_SystemHandle, _World); } - -#endif //!NET_DOTS } - } diff --git a/Unity.Entities/DebuggerDataAccess.cs b/Unity.Entities/DebuggerDataAccess.cs index dd2a2e6..0944e50 100644 --- a/Unity.Entities/DebuggerDataAccess.cs +++ b/Unity.Entities/DebuggerDataAccess.cs @@ -196,34 +196,14 @@ internal object GetComponentBoxedUnchecked(Entity entity, ComponentType type) int length = header->Length; - #if !NET_DOTS System.Array array = Array.CreateInstance(TypeManager.GetType(typeIndex), length); - #else - // no Array.CreateInstance in Tiny BCL - // This unfortunately means that the debugger display for this will be object[], because we can't - // create an array of the right type. But better than nothing, since the values are still viewable. - var array = new object[length]; - #endif - var elementSize = TypeManager.GetTypeInfo(typeIndex).ElementSize; byte* basePtr = BufferHeader.GetElementPointer(header); - #if !UNITY_DOTSRUNTIME var dstPtr = UnsafeUtility.PinGCArrayAndGetDataAddress(array, out var handle); UnsafeUtility.MemCpy(dstPtr, basePtr, elementSize * length); UnsafeUtility.ReleaseGCObject(handle); - #else - // DOTS Runtime doesn't have PinGCArrayAndGetDataAddress, because that's in Unity's Mono impl only - for (int i = 0; i < length; i++) - { - var item = TypeManager.ConstructComponentFromBuffer(type.TypeIndex, basePtr + elementSize * i); - #if !NET_DOTS - array.SetValue(item, i); - #else - array[i] = item; - #endif - } - #endif + return array; } else diff --git a/Unity.Entities/DefaultWorld.cs b/Unity.Entities/DefaultWorld.cs index 3116ae5..9cce352 100644 --- a/Unity.Entities/DefaultWorld.cs +++ b/Unity.Entities/DefaultWorld.cs @@ -163,7 +163,7 @@ public InitializationSystemGroup() /// protected override void OnUpdate() { -#if ENABLE_UNITY_COLLECTIONS_CHECKS && !UNITY_DOTSRUNTIME +#if ENABLE_UNITY_COLLECTIONS_CHECKS JobsUtility.ClearSystemIds(); #endif base.OnUpdate(); diff --git a/Unity.Entities/DefaultWorldInitialization.cs b/Unity.Entities/DefaultWorldInitialization.cs index 986e107..48dfe40 100644 --- a/Unity.Entities/DefaultWorldInitialization.cs +++ b/Unity.Entities/DefaultWorldInitialization.cs @@ -9,13 +9,9 @@ using UnityEngine; using Unity.Collections; using Unity.Profiling; -#if !UNITY_DOTSRUNTIME using UnityEngine.LowLevel; using UnityEngine.PlayerLoop; -#endif -#if !NET_DOTS using System.Linq; -#endif namespace Unity.Entities { @@ -36,7 +32,6 @@ public static class DefaultWorldInitialization internal static event Action DefaultWorldDestroyed; #pragma warning restore 0067 // unused variable -#if !UNITY_DOTSRUNTIME static bool s_UnloadOrPlayModeChangeShutdownRegistered = false; /// @@ -48,7 +43,6 @@ static void CleanupWorldBeforeSceneLoad() { DomainUnloadOrPlayModeChangeShutdown(); } -#endif /// /// Ensures the current World destruction on shutdown or when entering/exiting Play Mode or Domain Reload. @@ -67,7 +61,6 @@ static void CleanupWorldBeforeSceneLoad() /// static void RegisterUnloadOrPlayModeChangeShutdown() { -#if !UNITY_DOTSRUNTIME if (s_UnloadOrPlayModeChangeShutdownRegistered) return; @@ -82,12 +75,10 @@ static void RegisterUnloadOrPlayModeChangeShutdown() RuntimeApplication.RegisterFrameUpdateToCurrentPlayerLoop(); s_UnloadOrPlayModeChangeShutdownRegistered = true; -#endif } internal static void DomainUnloadOrPlayModeChangeShutdown() { -#if !UNITY_DOTSRUNTIME if (!s_UnloadOrPlayModeChangeShutdownRegistered) return; @@ -111,7 +102,6 @@ internal static void DomainUnloadOrPlayModeChangeShutdown() s_UnloadOrPlayModeChangeShutdownRegistered = false; DefaultWorldDestroyed?.Invoke(); -#endif } /// @@ -152,14 +142,12 @@ public static World Initialize(string defaultWorldName, bool editorWorld = false AddSystemToRootLevelSystemGroupsInternal(world, GetAllSystemTypeIndices(WorldSystemFilterFlags.Default, editorWorld)); -#if !UNITY_DOTSRUNTIME ScriptBehaviourUpdateOrder.AppendWorldToCurrentPlayerLoop(world); -#endif DefaultWorldInitialized?.Invoke(world); return world; } - + /// /// Adds the collection of systems to the world by injecting them into the root level system groups /// (InitializationSystemGroup, SimulationSystemGroup and PresentationSystemGroup). Prefer the version that @@ -174,7 +162,8 @@ public static void AddSystemsToRootLevelSystemGroups(World world, IEnumerable /// Adds the collection of systems to the world by injecting them into the root level system groups @@ -192,7 +181,7 @@ public static void AddSystemsToRootLevelSystemGroups(World world, IReadOnlyList< } AddSystemToRootLevelSystemGroupsInternal(world, systemTypeIndices); } - + /// /// Adds the collection of systems to the world by injecting them into the root level system groups @@ -208,9 +197,9 @@ public static void AddSystemsToRootLevelSystemGroups(World world, params Type[] { indices.Add(TypeManager.GetSystemTypeIndex(systemTypes[i])); } - AddSystemToRootLevelSystemGroupsInternal(world, indices); } + /// /// Adds the collection of systems to the world by injecting them into the root level system groups @@ -289,7 +278,7 @@ internal static unsafe void AddSystemToRootLevelSystemGroupsInternal(World wo } } - internal static void AddSystemToRootLevelSystemGroupsInternal(World world, NativeList systemTypesOrig) + internal static void AddSystemToRootLevelSystemGroupsInternal(World world, NativeList systemTypesOrig) { using var marker = new ProfilerMarker("AddSystems").Auto(); @@ -308,7 +297,7 @@ internal static void AddSystemToRootLevelSystemGroupsInternal(World world, Nativ private static ComponentSystemGroup FindGroup(World world, SystemTypeIndex systemType, TypeManager.SystemAttribute attr) { var groupTypeIndex = attr.TargetSystemTypeIndex; - + if (!TypeManager.IsSystemTypeIndex(groupTypeIndex) || !groupTypeIndex.IsGroup) { throw new InvalidOperationException($"Invalid [{nameof(UpdateInGroupAttribute)}] attribute for {systemType}: target group must be derived from {nameof(ComponentSystemGroup)}."); @@ -371,7 +360,7 @@ public static void DefaultLazyEditModeInitialize() /// /// Calculates a list of all systems filtered with WorldSystemFilterFlags, [DisableAutoCreation] etc. Prefer - /// GetAllSystemTypeIndices where possible to avoid extra reflection. + /// GetAllSystemTypeIndices where possible to avoid extra reflection. /// /// The filter flags to search for. /// Optionally require that [WorldSystemFilter(WorldSystemFilterFlags.Editor)] is present on the system. This is used when creating edit mode worlds. @@ -388,7 +377,7 @@ public static IReadOnlyList GetAllSystems(WorldSystemFilterFlags filterFla return ret; } - + /// /// Calculates a list of all systems filtered with WorldSystemFilterFlags, [DisableAutoCreation] etc. /// Prefer this over GetAllSystems if possible, to avoid extra reflection usage. @@ -403,7 +392,6 @@ public static NativeList GetAllSystemTypeIndices(WorldSystemFil static ICustomBootstrap CreateBootStrap() { -#if !UNITY_DOTSRUNTIME var bootstrapTypes = TypeManager.GetTypesDerivedFrom(typeof(ICustomBootstrap)); Type selectedType = null; @@ -424,9 +412,6 @@ static ICustomBootstrap CreateBootStrap() bootstrap = Activator.CreateInstance(selectedType) as ICustomBootstrap; return bootstrap; -#else - throw new Exception("This method should have been replaced by code-gen."); -#endif } } } diff --git a/Unity.Entities/DefaultWorldInitializationProxy.cs b/Unity.Entities/DefaultWorldInitializationProxy.cs index dc3a200..8a64ad2 100644 --- a/Unity.Entities/DefaultWorldInitializationProxy.cs +++ b/Unity.Entities/DefaultWorldInitializationProxy.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using UnityEngine; namespace Unity.Entities @@ -30,4 +29,3 @@ public void OnDisable() } } } -#endif diff --git a/Unity.Entities/Diff/EntityChangeSet.cs b/Unity.Entities/Diff/EntityChangeSet.cs index 8acf2da..58cf711 100644 --- a/Unity.Entities/Diff/EntityChangeSet.cs +++ b/Unity.Entities/Diff/EntityChangeSet.cs @@ -719,7 +719,6 @@ public unsafe void Dispose() } -#if !NET_DOTS [BurstCompile] internal static unsafe class EntityChangeSetFormatter { @@ -1073,5 +1072,4 @@ static void FormatComponentChange(ref EntityChangeSet changeSet, PackedComponent sb.AppendLine(nameInfoSet.Names[c.PackedEntityIndex].ToString()); } } -#endif } diff --git a/Unity.Entities/Diff/EntityDiffer.cs b/Unity.Entities/Diff/EntityDiffer.cs index 84982d4..abbf33e 100644 --- a/Unity.Entities/Diff/EntityDiffer.cs +++ b/Unity.Entities/Diff/EntityDiffer.cs @@ -469,6 +469,9 @@ static void UpdateBlobAssetCache(BlobAssetCache blobAssetCache, NativeList(Allocator.TempJob); + for (var i = 0; i < destroyedBlobAssets.Length; i++) { if (!batch->TryGetBlobAsset(destroyedBlobAssets[i].Hash, out _)) @@ -479,47 +482,44 @@ static void UpdateBlobAssetCache(BlobAssetCache blobAssetCache, NativeListReleaseBlobAssetImmediately(destroyedBlobAssets[i].Hash); - using (var keys = remap.GetKeyArray(Allocator.Temp)) - using (var values = remap.GetValueArray(Allocator.Temp)) + foreach (var keyValue in remap) { - for (var remapIndex = 0; remapIndex < values.Length; remapIndex++) - { - if (destroyedBlobAssets[i].Data != values[remapIndex].Data) - continue; - - remap.Remove(keys[remapIndex]); - break; - } + if (destroyedBlobAssets[i].Data != keyValue.Value.Data) + continue; + toDelete.Add(keyValue.Key); + break; } } + + { var deferredAdd = new NativeArray<(BlobAssetPtr key, BlobAssetPtr value)>(sameHashDifferentAddressBlobAssets.Length, Allocator.Temp); var deferredAddCount = 0; - for (int i = 0; i < sameHashDifferentAddressBlobAssets.Length; i++) + foreach (var update in sameHashDifferentAddressBlobAssets) { - var update = sameHashDifferentAddressBlobAssets[i]; - - using (var keys = remap.GetKeyArray(Allocator.Temp)) - using (var values = remap.GetValueArray(Allocator.Temp)) + foreach (var keyValue in remap) { - for (var remapIndex = 0; remapIndex < values.Length; remapIndex++) - { - if (values[remapIndex].Hash != update.Hash) - continue; - - if (keys[remapIndex].Data != update.Data) - { - remap.Remove(keys[remapIndex]); - deferredAdd[deferredAddCount] = (update, values[remapIndex]); - deferredAddCount += 1; - } + if (keyValue.Value.Hash != update.Hash) + continue; - break; + if (keyValue.Key.Data != update.Data) + { + toDelete.Add(keyValue.Key); + deferredAdd[deferredAddCount] = (update, keyValue.Value); + deferredAddCount += 1; } + + break; } } + foreach (var blobAssetPtr in toDelete) + { + remap.Remove(blobAssetPtr); + } + toDelete.Dispose(); + for (int i = 0; i < deferredAddCount; i++) { remap.Add(deferredAdd[i].key, deferredAdd[i].value); @@ -563,9 +563,6 @@ static void CheckEntityGuidComponent(EntityQueryDesc entityQueryDesc) throw new ArgumentException($"{nameof(EntityDiffer)} custom query requires an {nameof(EntityGuid)} component in the All filter."); } - /// - /// @TODO NET_DOTS does not support JobHandle.CombineDependencies with 3 arguments. - /// static JobHandle CombineDependencies(JobHandle job1, JobHandle job2, JobHandle job3) { var array = new NativeArray(3, Allocator.Temp) diff --git a/Unity.Entities/Diff/EntityDifferArchetypeChanges.cs b/Unity.Entities/Diff/EntityDifferArchetypeChanges.cs index 2bf6655..c2bd278 100644 --- a/Unity.Entities/Diff/EntityDifferArchetypeChanges.cs +++ b/Unity.Entities/Diff/EntityDifferArchetypeChanges.cs @@ -94,7 +94,7 @@ struct BuildChunkSequenceNumberMap : IJobParallelForDefer { [ReadOnly] public NativeList Chunks; [WriteOnly] public NativeParallelHashMap.ParallelWriter ChunksBySequenceNumber; - public void Execute(int index) => ChunksBySequenceNumber.TryAdd(Chunks[index].m_Chunk->SequenceNumber, Chunks[index]); + public void Execute(int index) => ChunksBySequenceNumber.TryAdd(Chunks[index].m_Chunk.SequenceNumber, Chunks[index]); } /// @@ -129,35 +129,35 @@ public void Execute() var srcChunk = default(ArchetypeChunk); // Any look for a matching chunk in the destination world. - SrcChunksBySequenceNumber.TryGetValue(dstChunk.m_Chunk->SequenceNumber, out srcChunk); + SrcChunksBySequenceNumber.TryGetValue(dstChunk.m_Chunk.SequenceNumber, out srcChunk); - if (srcChunk.m_Chunk == null) + if (srcChunk.m_Chunk == ChunkIndex.Null) { // This chunk exists in the destination world but NOT in the source world. // This means the chunk was simply destroyed. DestroyedChunks.Add(dstChunk); DestroyedChunkFlags.Add(ArchetypeChunkChangeFlags.None); DestroyedChunkEntityCounts.Add(destroyedChunkEntityCount); - destroyedChunkEntityCount += dstChunk.m_Chunk->Count; + destroyedChunkEntityCount += dstChunk.m_Chunk.Count; } else { - if (ChunksAreDifferent(dstChunk.m_Chunk, srcChunk.m_Chunk)) + if (ChunksAreDifferent(srcChunk.Archetype.Archetype, srcChunk.m_Chunk, dstChunk.Archetype.Archetype, dstChunk.m_Chunk)) { // The chunk exists in both worlds, but it has been changed in some way. // Treat this chunk as being destroyed and re-created. DestroyedChunks.Add(dstChunk); DestroyedChunkFlags.Add(ArchetypeChunkChangeFlags.Cloned); DestroyedChunkEntityCounts.Add(destroyedChunkEntityCount); - destroyedChunkEntityCount += dstChunk.m_Chunk->Count; + destroyedChunkEntityCount += dstChunk.m_Chunk.Count; CreatedChunks.Add(srcChunk); CreatedChunkFlags.Add(ArchetypeChunkChangeFlags.Cloned); CreatedChunkEntityCounts.Add(createdChunkEntityCounts); - createdChunkEntityCounts += srcChunk.m_Chunk->Count; + createdChunkEntityCounts += srcChunk.m_Chunk.Count; } - visitedChunks.TryAdd(srcChunk.m_Chunk->SequenceNumber, 1); + visitedChunks.TryAdd(srcChunk.m_Chunk.SequenceNumber, 1); } } @@ -167,14 +167,14 @@ public void Execute() var srcChunk = SrcChunks[i]; // We only care about chunks we have not visited yet. - if (!visitedChunks.TryGetValue(srcChunk.m_Chunk->SequenceNumber, out _)) + if (!visitedChunks.TryGetValue(srcChunk.m_Chunk.SequenceNumber, out _)) { // This chunk exists in the source world but NOT in the destination world. // This means the chunk was created. CreatedChunks.Add(srcChunk); CreatedChunkFlags.Add(ArchetypeChunkChangeFlags.Cloned); CreatedChunkEntityCounts.Add(createdChunkEntityCounts); - createdChunkEntityCounts += srcChunk.m_Chunk->Count; + createdChunkEntityCounts += srcChunk.m_Chunk.Count; } } @@ -182,22 +182,28 @@ public void Execute() DestroyedChunkEntityCounts.Add(destroyedChunkEntityCount); } - static bool ChunksAreDifferent(Chunk* srcChunk, Chunk* dstChunk) + static bool ChunksAreDifferent(Archetype* srcArchetype, ChunkIndex srcChunk, Archetype* dstArchetype, ChunkIndex dstChunk) { - if (srcChunk->Count != dstChunk->Count) + if (srcChunk.Count != dstChunk.Count) return true; - if (srcChunk->Archetype->TypesCount != dstChunk->Archetype->TypesCount) + if (srcArchetype->TypesCount != dstArchetype->TypesCount) return true; - var typeCount = srcChunk->Archetype->TypesCount; + var typeCount = srcArchetype->TypesCount; + + var srcChunkListIndex = srcChunk.ListIndex; + var dstChunkListIndex = dstChunk.ListIndex; for (var typeIndex = 0; typeIndex < typeCount; ++typeIndex) { - if (srcChunk->Archetype->Types[typeIndex] != dstChunk->Archetype->Types[typeIndex]) + if (srcArchetype->Types[typeIndex] != dstArchetype->Types[typeIndex]) return true; - if (srcChunk->GetChangeVersion(typeIndex) != dstChunk->GetChangeVersion(typeIndex)) + var srcVersion = srcArchetype->Chunks.GetChangeVersion(typeIndex, srcChunkListIndex); + var dstVersion = dstArchetype->Chunks.GetChangeVersion(typeIndex, dstChunkListIndex); + + if (srcVersion != dstVersion) return true; } diff --git a/Unity.Entities/Diff/EntityDifferBlobs.cs b/Unity.Entities/Diff/EntityDifferBlobs.cs index fa70f2c..b30262d 100644 --- a/Unity.Entities/Diff/EntityDifferBlobs.cs +++ b/Unity.Entities/Diff/EntityDifferBlobs.cs @@ -174,7 +174,7 @@ internal static BlobAssetsWithDistinctHash GetBlobAssetsWithDistinctHash( for (var chunkIndex = 0; chunkIndex < chunks.Length; chunkIndex++) { var chunk = chunks[chunkIndex].m_Chunk; - var archetype = chunk->Archetype; + var archetype = entityComponentStore->GetArchetype(chunk); // skip this chunk only if we are _certain_ there are no blob asset refs. if (!archetype->HasBlobAssetRefs) @@ -194,7 +194,7 @@ internal static BlobAssetsWithDistinctHash GetBlobAssetsWithDistinctHash( if (!typeInfo.HasBlobAssetRefs) continue; - var chunkBuffer = chunk->Buffer; + var chunkBuffer = chunk.Buffer; var subArrayOffset = archetype->Offsets[typeIndexInArchetype]; var componentArrayStart = chunkBuffer + subArrayOffset; @@ -250,7 +250,7 @@ internal static BlobAssetsWithDistinctHash GetBlobAssetsWithDistinctHash( } } - var sharedComponentValues = chunk->SharedComponentValues; + var sharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); for (var i = 0; i < archetype->NumSharedComponents; i++) { var sharedComponentIndex = sharedComponentValues[i]; @@ -275,8 +275,8 @@ internal static BlobAssetsWithDistinctHash GetBlobAssetsWithDistinctHash( for (var chunkIndex = 0; chunkIndex < chunks.Length; chunkIndex++) { var chunk = chunks[chunkIndex].m_Chunk; - var archetype = chunk->Archetype; - var sharedComponentValues = chunk->SharedComponentValues; + var archetype = entityComponentStore->GetArchetype(chunk); + var sharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); for (var i = 0; i < archetype->NumSharedComponents; i++) { diff --git a/Unity.Entities/Diff/EntityDifferClearMissingReferences.cs b/Unity.Entities/Diff/EntityDifferClearMissingReferences.cs index d25f2dc..1e2f555 100644 --- a/Unity.Entities/Diff/EntityDifferClearMissingReferences.cs +++ b/Unity.Entities/Diff/EntityDifferClearMissingReferences.cs @@ -16,7 +16,7 @@ public void Execute(int index) { var chunk = Chunks[index].m_Chunk; - ChunkDataUtility.ClearMissingReferences(chunk); + ChunkDataUtility.ClearMissingReferences(Chunks[index].Archetype.Archetype, chunk); } } diff --git a/Unity.Entities/Diff/EntityDifferComponentChanges.cs b/Unity.Entities/Diff/EntityDifferComponentChanges.cs index 20d3c06..0a44f83 100644 --- a/Unity.Entities/Diff/EntityDifferComponentChanges.cs +++ b/Unity.Entities/Diff/EntityDifferComponentChanges.cs @@ -19,6 +19,18 @@ namespace Unity.Entities [BurstCompile] static unsafe partial class EntityDiffer { + static bool TryGetEntityGuidComponent(EntityComponentStore* ecs, Entity entity, TypeIndex entityGuidTypeIndex, out EntityGuid entityGuid) + { + entityGuid = default; + if (!ecs->HasComponent(entity, entityGuidTypeIndex)) + { + return false; + } + + entityGuid = *(EntityGuid*)ecs->GetComponentDataWithTypeRO(entity, entityGuidTypeIndex); + return true; + } + // This value has to be power of two. public const int ComponentChangesBatchCount = 128; public struct DeferredSharedComponentChange @@ -409,6 +421,9 @@ struct GatherComponentChangesBuildPacked : IJob [ReadOnly] public NativeList ModifiedEntities; [ReadOnly] [NativeDisableUnsafePtrRestriction] public TypeManager.TypeInfo* TypeInfo; + [NativeDisableUnsafePtrRestriction] public EntityComponentStore* AfterEntityComponentStore; + [NativeDisableUnsafePtrRestriction] public EntityComponentStore* BeforeEntityComponentStore; + public PackedEntityGuidsCollection Entities; public PackedCollection ComponentTypes; @@ -437,7 +452,7 @@ public void Execute() var entityGuid = CreatedEntities[i].EntityGuid; var afterEntity = CreatedEntities[i].AfterEntityInChunk; var afterChunk = afterEntity.Chunk; - var afterArchetype = afterChunk->Archetype; + var afterArchetype = AfterEntityComponentStore->GetArchetype(afterChunk); var afterTypesCount = afterArchetype->TypesCount; Entities.List.Add(entityGuid); @@ -448,7 +463,7 @@ public void Execute() { var afterTypeInArchetype = afterArchetype->Types[afterIndexInTypeArray]; - if (afterTypeInArchetype.IsCleanupComponent || afterTypeInArchetype.IsBakeOnlyType) + if (afterTypeInArchetype.IsCleanupComponent || afterTypeInArchetype.IsBakeOnlyType || afterTypeInArchetype.IsChunkComponent) continue; AddStableTypeHash(afterTypeInArchetype.TypeIndex); @@ -466,13 +481,13 @@ public void Execute() var afterEntity = modification.AfterEntityInChunk; var afterChunk = afterEntity.Chunk; - var afterArchetype = afterChunk->Archetype; + var afterArchetype = AfterEntityComponentStore->GetArchetype(afterChunk); var afterTypesCount = afterArchetype->TypesCount; var beforeEntity = modification.BeforeEntityInChunk; var beforeChunk = beforeEntity.Chunk; - var beforeArchetype = beforeChunk->Archetype; + var beforeArchetype = BeforeEntityComponentStore->GetArchetype(beforeChunk); var beforeTypesCount = beforeArchetype->TypesCount; Entities.List.Add(entityGuid); @@ -499,7 +514,7 @@ public void Execute() { var beforeComponentTypeInArchetype = beforeArchetype->Types[beforeTypeIndexInArchetype]; - if (beforeComponentTypeInArchetype.IsCleanupComponent || beforeComponentTypeInArchetype.IsBakeOnlyType) + if (beforeComponentTypeInArchetype.IsCleanupComponent || beforeComponentTypeInArchetype.IsBakeOnlyType || beforeComponentTypeInArchetype.IsChunkComponent) continue; var beforeTypeIndex = beforeComponentTypeInArchetype.TypeIndex; @@ -526,6 +541,8 @@ struct GatherComponentChanges // Atomic outputs written from the cached data public GatherComponentChangesOutput OutputData; public NativeHashMap AddedArchetypes; + public EntityComponentStore* AfterEntityComponentStore; + public EntityComponentStore* BeforeEntityComponentStore; public void ProcessRange(int index) { @@ -542,7 +559,7 @@ public void ProcessRange(int index) var entityGuid = ReadData.CreatedEntities[i].EntityGuid; var afterEntity = ReadData.CreatedEntities[i].AfterEntityInChunk; var afterChunk = afterEntity.Chunk; - var afterArchetype = afterChunk->Archetype; + var afterArchetype = AfterEntityComponentStore->GetArchetype(afterChunk); var afterTypesCount = afterArchetype->TypesCount; var archetypeHash = afterArchetype->StableHash; @@ -571,7 +588,7 @@ public void ProcessRange(int index) { var afterTypeInArchetype = afterArchetype->Types[indexInTypeArray]; - if (afterTypeInArchetype.IsCleanupComponent || afterTypeInArchetype.IsBakeOnlyType) + if (afterTypeInArchetype.IsCleanupComponent || afterTypeInArchetype.IsBakeOnlyType || afterTypeInArchetype.IsChunkComponent) continue; ValidateTypeForSerialization(afterTypeInArchetype, entityGuid); @@ -594,7 +611,7 @@ public void ProcessRange(int index) { var afterTypeInArchetype = afterArchetype->Types[afterIndexInTypeArray]; - if (afterTypeInArchetype.IsCleanupComponent || afterTypeInArchetype.IsBakeOnlyType) + if (afterTypeInArchetype.IsCleanupComponent || afterTypeInArchetype.IsBakeOnlyType || afterTypeInArchetype.IsChunkComponent) continue; // This handles special component types that need additional/special adding @@ -636,12 +653,12 @@ public void ProcessRange(int index) var afterEntity = modification.AfterEntityInChunk; var afterChunk = afterEntity.Chunk; - var afterArchetype = afterChunk->Archetype; + var afterArchetype = AfterEntityComponentStore->GetArchetype(afterChunk); var afterTypesCount = afterArchetype->TypesCount; var beforeEntity = modification.BeforeEntityInChunk; var beforeChunk = beforeEntity.Chunk; - var beforeArchetype = beforeChunk->Archetype; + var beforeArchetype = BeforeEntityComponentStore->GetArchetype(beforeChunk); var beforeTypesCount = beforeArchetype->TypesCount; for (var afterIndexInTypeArray = 1; afterIndexInTypeArray < afterTypesCount; afterIndexInTypeArray++) @@ -677,9 +694,14 @@ public void ProcessRange(int index) continue; } - if (!afterTypeInArchetype.IsManagedComponent && modification.CanCompareChunkVersions && afterChunk->GetChangeVersion(afterIndexInTypeArray) == beforeChunk->GetChangeVersion(beforeIndexInTypeArray)) + if (!afterTypeInArchetype.IsManagedComponent && modification.CanCompareChunkVersions) { - continue; + var afterVersion = afterArchetype->Chunks.GetChangeVersion(afterIndexInTypeArray, afterChunk.ListIndex); + var beforeVersion = beforeArchetype->Chunks.GetChangeVersion(beforeIndexInTypeArray, beforeChunk.ListIndex); + if (afterVersion == beforeVersion) + { + continue; + } } SetComponentData( @@ -700,7 +722,7 @@ public void ProcessRange(int index) { var beforeComponentTypeInArchetype = beforeArchetype->Types[beforeTypeIndexInArchetype]; - if (beforeComponentTypeInArchetype.IsCleanupComponent || beforeComponentTypeInArchetype.IsBakeOnlyType) + if (beforeComponentTypeInArchetype.IsCleanupComponent || beforeComponentTypeInArchetype.IsBakeOnlyType || beforeComponentTypeInArchetype.IsChunkComponent) { continue; } @@ -717,7 +739,7 @@ public void ProcessRange(int index) } void AddComponentData( - Chunk* afterChunk, + ChunkIndex afterChunk, Archetype* afterArchetype, ComponentTypeInArchetype afterTypeInArchetype, int afterIndexInTypeArray, @@ -728,6 +750,7 @@ void AddComponentData( bool addComponent) { var packedComponent = PackComponent(entityGuid, afterTypeInArchetype.TypeIndex, tableHint, entryHint); + var chunkBuffer = afterChunk.Buffer; if (addComponent) { @@ -736,8 +759,8 @@ void AddComponentData( if (afterTypeInArchetype.IsSharedComponent) { - var offset = afterIndexInTypeArray - afterChunk->Archetype->FirstSharedComponent; - var sharedComponentIndex = afterChunk->GetSharedComponentValue(offset); + var offset = afterIndexInTypeArray - AfterEntityComponentStore->GetArchetype(afterChunk)->FirstSharedComponent; + var sharedComponentIndex = afterArchetype->Chunks.GetSharedComponentValue(offset, afterChunk.ListIndex); // No managed objects in burst land. Do what we can a defer the actual unpacking until later. AddendSharedComponentData(entityGuid, afterTypeInArchetype.TypeIndex, sharedComponentIndex); @@ -746,7 +769,7 @@ void AddComponentData( if (afterTypeInArchetype.IsManagedComponent) { - var afterManagedComponentIndex = ((int*)(ChunkDataUtility.GetChunkBuffer(afterChunk) + afterArchetype->Offsets[afterIndexInTypeArray]))[afterEntityIndexInChunk]; + var afterManagedComponentIndex = ((int*)(chunkBuffer + afterArchetype->Offsets[afterIndexInTypeArray]))[afterEntityIndexInChunk]; AppendManagedComponentData(entityGuid, afterTypeInArchetype.TypeIndex, afterManagedComponentIndex); return; } @@ -754,7 +777,7 @@ void AddComponentData( int isEnabled = -1; if (afterTypeInArchetype.IsEnableable) { - var isComponentEnabled = ChunkDataUtility.GetEnabledRefRO(afterChunk, afterIndexInTypeArray); + var isComponentEnabled = ChunkDataUtility.GetEnabledRefRO(afterChunk, afterArchetype, afterIndexInTypeArray); // Default value of an enableable component is true, so we only need to process new components that are false isEnabled = isComponentEnabled.IsSet(afterEntityIndexInChunk) ? -1 : 0; } @@ -774,7 +797,7 @@ void AddComponentData( if (afterTypeInArchetype.IsBuffer) { var sizeOf = afterArchetype->SizeOfs[afterIndexInTypeArray]; - var buffer = (BufferHeader*)(ChunkDataUtility.GetChunkBuffer(afterChunk) + afterArchetype->Offsets[afterIndexInTypeArray] + afterEntityIndexInChunk * sizeOf); + var buffer = (BufferHeader*)(chunkBuffer + afterArchetype->Offsets[afterIndexInTypeArray] + afterEntityIndexInChunk * sizeOf); var length = buffer->Length; if (length == 0) @@ -790,9 +813,7 @@ void AddComponentData( if (afterTypeInArchetype.TypeIndex == ReadData.LinkedEntityGroupTypeIndex) { - // Magic in AddComponent already put a self-reference at the top of the buffer, so there's no need for us to add it. - // The rest of the elements should be interpreted as LinkedEntityGroupAdditions. - for (var elementIndex = 1; elementIndex < length; elementIndex++) + for (var elementIndex = 0; elementIndex < length; elementIndex++) { var childEntity = ((Entity*)elementPtr)[elementIndex]; var childEntityGuid = GetEntityGuid(ReadData.AfterEntityComponentStore, ReadData.EntityGuidTypeIndex, childEntity); @@ -815,7 +836,7 @@ void AddComponentData( { var typeInfo = &ReadData.TypeInfo[afterTypeInArchetype.TypeIndex.Index]; var sizeOf = afterArchetype->SizeOfs[afterIndexInTypeArray]; - var ptr = ChunkDataUtility.GetChunkBuffer(afterChunk) + afterArchetype->Offsets[afterIndexInTypeArray] + afterEntityIndexInChunk * sizeOf; + var ptr = chunkBuffer + afterArchetype->Offsets[afterIndexInTypeArray] + afterEntityIndexInChunk * sizeOf; AppendComponentData(packedComponent, ptr, sizeOf, isEnabled); ExtractPatches(typeInfo, packedComponent, ptr, 1); } @@ -829,25 +850,28 @@ private static void AssertArchetypeSizeOfsMatch(Archetype* beforeArchetype, int } void SetComponentData( - Chunk* afterChunk, + ChunkIndex afterChunk, Archetype* afterArchetype, ComponentTypeInArchetype afterTypeInArchetype, int afterIndexInTypeArray, int afterEntityIndexInChunk, - Chunk* beforeChunk, + ChunkIndex beforeChunk, Archetype* beforeArchetype, int beforeIndexInTypeArray, int beforeEntityIndexInChunk, EntityGuid entityGuid, int entryHint) { + var afterChunkBuffer = afterChunk.Buffer; + var beforeChunkBuffer = beforeChunk.Buffer; + if (afterTypeInArchetype.IsSharedComponent) { - var beforeOffset = beforeIndexInTypeArray - beforeChunk->Archetype->FirstSharedComponent; - var beforeSharedComponentIndex = beforeChunk->GetSharedComponentValue(beforeOffset); + var beforeOffset = beforeIndexInTypeArray - BeforeEntityComponentStore->GetArchetype(beforeChunk)->FirstSharedComponent; + var beforeSharedComponentIndex = beforeArchetype->Chunks.GetSharedComponentValue(beforeOffset, beforeChunk.ListIndex); - var afterOffset = afterIndexInTypeArray - afterChunk->Archetype->FirstSharedComponent; - var afterSharedComponentIndex = afterChunk->GetSharedComponentValue(afterOffset); + var afterOffset = afterIndexInTypeArray - AfterEntityComponentStore->GetArchetype(afterChunk)->FirstSharedComponent; + var afterSharedComponentIndex = afterArchetype->Chunks.GetSharedComponentValue(afterOffset, afterChunk.ListIndex); // No managed objects in burst land. Do what we can and defer the actual unpacking until later. AddendSharedComponentData(entityGuid, afterTypeInArchetype.TypeIndex, afterSharedComponentIndex, beforeSharedComponentIndex); @@ -856,8 +880,8 @@ void SetComponentData( if (afterTypeInArchetype.IsManagedComponent) { - var afterManagedComponentIndex = ((int*)(ChunkDataUtility.GetChunkBuffer(afterChunk) + afterArchetype->Offsets[afterIndexInTypeArray]))[afterEntityIndexInChunk]; - var beforeManagedComponentIndex = ((int*)(ChunkDataUtility.GetChunkBuffer(beforeChunk) + beforeArchetype->Offsets[beforeIndexInTypeArray]))[beforeEntityIndexInChunk]; + var afterManagedComponentIndex = ((int*)(afterChunkBuffer + afterArchetype->Offsets[afterIndexInTypeArray]))[afterEntityIndexInChunk]; + var beforeManagedComponentIndex = ((int*)(beforeChunkBuffer + beforeArchetype->Offsets[beforeIndexInTypeArray]))[beforeEntityIndexInChunk]; AppendManagedComponentData(entityGuid, afterTypeInArchetype.TypeIndex, afterManagedComponentIndex, beforeManagedComponentIndex); return; @@ -866,8 +890,8 @@ void SetComponentData( int isEnabledAfter = -1; if (afterTypeInArchetype.IsEnableable) { - AreEnableableComponentsEqual(afterChunk, afterIndexInTypeArray, afterEntityIndexInChunk, - beforeChunk, beforeIndexInTypeArray, beforeEntityIndexInChunk, out isEnabledAfter); + AreEnableableComponentsEqual(afterChunk, afterArchetype, afterIndexInTypeArray, afterEntityIndexInChunk, + beforeChunk, beforeArchetype, beforeIndexInTypeArray, beforeEntityIndexInChunk, out isEnabledAfter); } // IMPORTANT This means `IsZeroSizedInChunk` which is always true for shared components. @@ -884,7 +908,7 @@ void SetComponentData( if (afterTypeInArchetype.IsBuffer) { - var beforeBuffer = (BufferHeader*)(ChunkDataUtility.GetChunkBuffer(beforeChunk) + var beforeBuffer = (BufferHeader*)(beforeChunkBuffer + beforeArchetype->Offsets[beforeIndexInTypeArray] + beforeEntityIndexInChunk * beforeArchetype->SizeOfs[beforeIndexInTypeArray]); @@ -892,7 +916,7 @@ void SetComponentData( var beforeElementPtr = BufferHeader.GetElementPointer(beforeBuffer); var beforeLength = beforeBuffer->Length; - var afterBuffer = (BufferHeader*)(ChunkDataUtility.GetChunkBuffer(afterChunk) + var afterBuffer = (BufferHeader*)(afterChunkBuffer + afterArchetype->Offsets[afterIndexInTypeArray] + afterEntityIndexInChunk * afterArchetype->SizeOfs[afterIndexInTypeArray]); @@ -944,12 +968,12 @@ void SetComponentData( AssertArchetypeSizeOfsMatch(beforeArchetype, beforeIndexInTypeArray, afterArchetype, afterIndexInTypeArray); - var beforeAddress = ChunkDataUtility.GetChunkBuffer(beforeChunk) + var beforeAddress = beforeChunkBuffer + beforeArchetype->Offsets[beforeIndexInTypeArray] + beforeArchetype->SizeOfs[beforeIndexInTypeArray] * beforeEntityIndexInChunk; - var afterAddress = ChunkDataUtility.GetChunkBuffer(afterChunk) + var afterAddress = afterChunkBuffer + afterArchetype->Offsets[afterIndexInTypeArray] + afterArchetype->SizeOfs[afterIndexInTypeArray] * afterEntityIndexInChunk; @@ -1003,13 +1027,13 @@ static bool ShouldSkip(int offset, ref int nextOffsetIndex, int offsetCount, Typ } - bool AreEnableableComponentsEqual(Chunk* afterChunk, int afterIndexInTypeArray, int afterEntityIndexInChunk, - Chunk* beforeChunk, int beforeIndexInTypeArray, int beforeEntityIndexInChunk, out int enabled) + bool AreEnableableComponentsEqual(ChunkIndex afterChunk, Archetype* afterArchetype, int afterIndexInTypeArray, int afterEntityIndexInChunk, + ChunkIndex beforeChunk, Archetype* beforeArchetype, int beforeIndexInTypeArray, int beforeEntityIndexInChunk, out int enabled) { - var isComponentEnabledAfter = ChunkDataUtility.GetEnabledRefRO(afterChunk, afterIndexInTypeArray); + var isComponentEnabledAfter = ChunkDataUtility.GetEnabledRefRO(afterChunk, afterArchetype, afterIndexInTypeArray); bool isEnabledAfter = isComponentEnabledAfter.IsSet(afterEntityIndexInChunk); - var isComponentEnabledBefore = ChunkDataUtility.GetEnabledRefRO(beforeChunk, beforeIndexInTypeArray); + var isComponentEnabledBefore = ChunkDataUtility.GetEnabledRefRO(beforeChunk, beforeArchetype, beforeIndexInTypeArray); bool isEnabledBefore = isComponentEnabledBefore.IsSet(beforeEntityIndexInChunk); bool equal = isEnabledAfter == isEnabledBefore; @@ -1140,8 +1164,8 @@ bool EntityGuidsAreDifferent( var beforeEntity = *(Entity*) (beforeAddress + offset); // If the entity has no guid, then guid will be null (desired) - ReadData.BeforeEntityComponentStore->TryGetComponent(beforeEntity, ReadData.EntityGuidTypeIndex, out var beforeGuid); - ReadData.AfterEntityComponentStore->TryGetComponent(afterEntity, ReadData.EntityGuidTypeIndex, out var afterGuid); + TryGetEntityGuidComponent(ReadData.BeforeEntityComponentStore, beforeEntity, ReadData.EntityGuidTypeIndex, out var beforeGuid); + TryGetEntityGuidComponent(ReadData.AfterEntityComponentStore, afterEntity, ReadData.EntityGuidTypeIndex, out var afterGuid); if (!beforeGuid.Equals(afterGuid)) return true; } @@ -1186,7 +1210,7 @@ void ExtractEntityReferencePatches( var entity = *(Entity*)(afterAddress + offset); // If the entity has no guid, then guid will be null (desired) - ReadData.AfterEntityComponentStore->TryGetComponent(entity, ReadData.EntityGuidTypeIndex, out var entityGuid); + TryGetEntityGuidComponent(ReadData.AfterEntityComponentStore, entity, ReadData.EntityGuidTypeIndex, out var entityGuid); CacheData.EntityReferencePatches.Add(new EntityReferenceChange { @@ -1356,7 +1380,7 @@ private void ThrowOnEntityGuidMissing() EntityGuid GetEntityGuid(EntityComponentStore* entityComponentStore, TypeIndex entityGuidTypeIndex, Entity entity) { - if (!entityComponentStore->TryGetComponent(entity, entityGuidTypeIndex, out var result)) + if (!TryGetEntityGuidComponent(entityComponentStore, entity, entityGuidTypeIndex, out var result)) { ThrowOnEntityGuidMissing(); } @@ -1488,6 +1512,8 @@ struct GatherComponentChangesJob : IJob { public GatherComponentChangesReadOnlyData ReadData; public GatherComponentChangesOutput OutputData; + [NativeDisableUnsafePtrRestriction] public EntityComponentStore* AfterEntityComponentStore; + [NativeDisableUnsafePtrRestriction] public EntityComponentStore* BeforeEntityComponentStore; public void Execute() { @@ -1526,7 +1552,9 @@ public void Execute() ReadData = ReadData, CacheData = cacheData, OutputData = OutputData, - AddedArchetypes = addedArchetypes + AddedArchetypes = addedArchetypes, + AfterEntityComponentStore = AfterEntityComponentStore, + BeforeEntityComponentStore = BeforeEntityComponentStore, }; int count = ReadData.CreatedEntities.Length + ReadData.ModifiedEntities.Length; @@ -1640,6 +1668,8 @@ static ComponentChanges GetComponentChanges( TypeInfo = TypeManager.GetTypeInfoPointer(), Entities = readOnlyData.Entities, ComponentTypes = readOnlyData.ComponentTypes, + AfterEntityComponentStore = entityChanges.AfterEntityManager.GetCheckedEntityDataAccess()->EntityComponentStore, + BeforeEntityComponentStore = entityChanges.BeforeEntityManager.GetCheckedEntityDataAccess()->EntityComponentStore, }.Run(); if (count >= 1) @@ -1647,7 +1677,9 @@ static ComponentChanges GetComponentChanges( new GatherComponentChangesJob { ReadData = readOnlyData, - OutputData = outputData + OutputData = outputData, + AfterEntityComponentStore = entityChanges.AfterEntityManager.GetCheckedEntityDataAccess()->EntityComponentStore, + BeforeEntityComponentStore = entityChanges.BeforeEntityManager.GetCheckedEntityDataAccess()->EntityComponentStore, }.Run(); } #if UNITY_EDITOR @@ -2032,7 +2064,7 @@ bool TryGetEntityGuid(EntityComponentStore* entityComponentStoreEntity, Entity e entityGuid = default; if (entityComponentStoreEntity->Exists(entity) && - entityComponentStoreEntity->TryGetComponent(entity, EntityGuidTypeIndex, out entityGuid)) + TryGetEntityGuidComponent(entityComponentStoreEntity, entity, EntityGuidTypeIndex, out entityGuid)) { return true; } @@ -2055,7 +2087,7 @@ public void Execute() int guidIndex = 0; for (var i = 0; i < CreatedEntities.Length; i++) { - var afterEntity = ChunkDataUtility.GetEntityFromEntityInChunk(CreatedEntities[i].AfterEntityInChunk); + var afterEntity = ChunkDataUtility.GetEntityFromEntityInChunk(AfterEntityComponentStore->GetArchetype(CreatedEntities[i].AfterEntityInChunk.Chunk), CreatedEntities[i].AfterEntityInChunk); AfterEntityComponentStore->GetName(afterEntity, out namesPtr[nameIndex++]); entitiesLookup.Add(CreatedEntities[i].EntityGuid); @@ -2104,7 +2136,9 @@ public void Execute() // They will not exist in the after world so use the before world. for (var i = 0; i < DestroyedEntities.Length; i++) { - var beforeEntity = ChunkDataUtility.GetEntityFromEntityInChunk(DestroyedEntities[i].BeforeEntityInChunk); + var beforeArchetype = + BeforeEntityComponentStore->GetArchetype(DestroyedEntities[i].BeforeEntityInChunk.Chunk); + var beforeEntity = ChunkDataUtility.GetEntityFromEntityInChunk(beforeArchetype, DestroyedEntities[i].BeforeEntityInChunk); BeforeEntityComponentStore->GetName(beforeEntity, out namesPtr[nameIndex++]); } @@ -2201,10 +2235,8 @@ public void ExtractPatches( protected override void VisitProperty(Property property, ref TContainer container, ref TValue value) { -#if !UNITY_DOTSRUNTIME if (typeof(UnityEngine.Object).IsAssignableFrom(typeof(TValue))) return; -#endif base.VisitProperty(property, ref container, ref value); } diff --git a/Unity.Entities/Diff/EntityDifferCopyAndReplace.cs b/Unity.Entities/Diff/EntityDifferCopyAndReplace.cs index 74c9dad..7469cc6 100644 --- a/Unity.Entities/Diff/EntityDifferCopyAndReplace.cs +++ b/Unity.Entities/Diff/EntityDifferCopyAndReplace.cs @@ -27,10 +27,10 @@ public void Execute(int index) var srcChunk = SrcChunks[index].m_Chunk; var dstChunk = DstChunks[index].m_Chunk; - var srcArchetype = srcChunk->Archetype; - var dstArchetype = dstChunk->Archetype; + var srcArchetype = SrcEntityComponentStore->GetArchetype(srcChunk); + var dstArchetype = DstEntityComponentStore->GetArchetype(dstChunk); - ChunkDataUtility.CloneChangeVersions(srcArchetype, srcChunk->ListIndex, dstArchetype, dstChunk->ListIndex); + ChunkDataUtility.CloneChangeVersions(srcArchetype, srcChunk.ListIndex, dstArchetype, dstChunk.ListIndex); DstEntityComponentStore->AddExistingEntitiesInChunk(dstChunk); } @@ -62,7 +62,7 @@ internal static void CopyAndReplaceChunks( { foreach (var chunk in allDstChunks) { - var metaEntity = chunk.m_Chunk->metaChunkEntity; + var metaEntity = chunk.m_Chunk.MetaChunkEntity; if (metaEntity != Entity.Null) { if (dstEntityManager.Exists(metaEntity)) @@ -90,14 +90,15 @@ public void Execute() for (var i = 0; i < Chunks.Length; i++) { var chunk = Chunks[i].m_Chunk; - var count = chunk->Count; - ChunkDataUtility.DeallocateBuffers(chunk); + var archetype = Chunks[i].Archetype.Archetype; + var count = chunk.Count; + ChunkDataUtility.DeallocateBuffers(archetype, chunk); ecs->DeallocateManagedComponents(chunk, 0, count); - chunk->Archetype->EntityCount -= chunk->Count; + archetype->EntityCount -= chunk.Count; ecs->FreeEntities(chunk); - ChunkDataUtility.SetChunkCountKeepMetaChunk(chunk, 0); + ChunkDataUtility.SetChunkCountKeepMetaChunk(archetype, chunk, 0); } } } @@ -129,17 +130,17 @@ public void Execute() { var indices = new UnsafeParallelHashSet(128, Allocator.Temp); for (int i = 0; i < Chunks.Length; i++) - HandleChunk(Chunks[i].m_Chunk, ref indices); + HandleChunk(Chunks[i].Archetype.Archetype, Chunks[i].m_Chunk, ref indices); SharedComponentIndices.AddRange(indices.ToNativeArray(Allocator.Temp)); } - void HandleChunk(Chunk* srcChunk, ref UnsafeParallelHashSet indices) + void HandleChunk(Archetype* srcArchetype, ChunkIndex srcChunk, ref UnsafeParallelHashSet indices) { - var srcArchetype = srcChunk->Archetype; var n = srcArchetype->NumSharedComponents; var sharedIndices = stackalloc int[srcArchetype->NumSharedComponents]; - srcChunk->SharedComponentValues.CopyTo(sharedIndices, 0, srcArchetype->NumSharedComponents); + var sharedComponents = srcArchetype->Chunks.GetSharedComponentValues(srcChunk.ListIndex); + sharedComponents.CopyTo(sharedIndices, 0, srcArchetype->NumSharedComponents); for (int i = 0; i < n; i++) { indices.Add(sharedIndices[i]); @@ -173,21 +174,23 @@ public void Execute() void HandleChunk(int idx, EntityComponentStore* dstEntityComponentStore, UnsafeParallelHashMap sharedComponentRemap) { var srcChunk = Chunks[idx].m_Chunk; - var numSharedComponents = srcChunk->Archetype->NumSharedComponents; + var srcArchetype = Chunks[idx].Archetype.Archetype; + var numSharedComponents = srcArchetype->NumSharedComponents; var dstSharedIndices = stackalloc int[numSharedComponents]; - srcChunk->SharedComponentValues.CopyTo(dstSharedIndices, 0, numSharedComponents); + var srcSharedComponents = srcArchetype->Chunks.GetSharedComponentValues(srcChunk.ListIndex); + srcSharedComponents.CopyTo(dstSharedIndices, 0, numSharedComponents); for (int i = 0; i < numSharedComponents; i++) dstSharedIndices[i] = sharedComponentRemap[dstSharedIndices[i]]; - var srcArchetype = srcChunk->Archetype; var dstArchetype = dstEntityComponentStore->GetOrCreateArchetype(srcArchetype->Types, srcArchetype->TypesCount); var dstChunk = dstEntityComponentStore->GetCleanChunkNoMetaChunk(dstArchetype, dstSharedIndices); - dstChunk->metaChunkEntity = srcChunk->metaChunkEntity; - ChunkDataUtility.SetChunkCountKeepMetaChunk(dstChunk, srcChunk->Count); - dstChunk->Archetype->EntityCount += srcChunk->Count; - dstChunk->SequenceNumber = srcChunk->SequenceNumber; + dstChunk.MetaChunkEntity = srcChunk.MetaChunkEntity; + var srcChunkCount = srcChunk.Count; + ChunkDataUtility.SetChunkCountKeepMetaChunk(dstArchetype, dstChunk, srcChunkCount); + dstArchetype->EntityCount += srcChunkCount; + dstChunk.SequenceNumber = srcChunk.SequenceNumber; - ClonedChunks[idx] = new ArchetypeChunk {m_Chunk = dstChunk}; + ClonedChunks[idx] = new ArchetypeChunk { m_Chunk = dstChunk }; } } @@ -203,9 +206,8 @@ public void Execute(int index) { var srcChunk = Chunks[index].m_Chunk; var dstChunk = ClonedChunks[index].m_Chunk; - var copySize = Chunk.GetChunkBufferSize(); - UnsafeUtility.MemCpy((byte*)dstChunk + Chunk.kBufferOffset, (byte*)srcChunk + Chunk.kBufferOffset, copySize); - BufferHeader.PatchAfterCloningChunk(dstChunk); + UnsafeUtility.MemCpy(dstChunk.Buffer, srcChunk.Buffer, Chunk.kChunkBufferSize); + BufferHeader.PatchAfterCloningChunk(Chunks[index].Archetype.Archetype, dstChunk.Buffer, dstChunk.Count); } } @@ -269,31 +271,34 @@ static void CloneAndAddChunks(EntityManager srcEntityManager, EntityManager dstE { var srcChunk = chunks[i].m_Chunk; var dstChunk = cloned[i].m_Chunk; - ChunkDataUtility.CloneEnabledBits(srcChunk, 0, dstChunk, 0, srcChunk->Count); - Assertions.Assert.AreEqual(srcChunk->Count, dstChunk->Count); + var srcArchetype = srcAccess->EntityComponentStore->GetArchetype(srcChunk); + var dstArchetype = dstAccess->EntityComponentStore->GetArchetype(dstChunk); + var srcChunkCount = srcChunk.Count; + ChunkDataUtility.CloneEnabledBits(srcChunk, srcArchetype, 0, dstChunk, dstArchetype, 0, srcChunkCount); + Assertions.Assert.AreEqual(srcChunkCount, dstChunk.Count); // Can't do this until chunk count is up to date and padding bits are clear, // which is implicitly the case at this point - ChunkDataUtility.UpdateChunkDisabledEntityCounts(dstChunk); + ChunkDataUtility.UpdateChunkDisabledEntityCounts(dstChunk, dstArchetype); } s_CopyManagedComponentsMarker.Begin(); for (int i = 0; i < cloned.Length; i++) { var dstChunk = cloned[i].m_Chunk; - var dstArchetype = dstChunk->Archetype; + var dstArchetype = dstAccess->EntityComponentStore->GetArchetype(dstChunk); var numManagedComponents = dstArchetype->NumManagedComponents; var hasCompanionComponents = dstArchetype->HasCompanionComponents; for (int t = 0; t < numManagedComponents; ++t) { - int indexInArchetype = t + dstChunk->Archetype->FirstManagedComponent; - var offset = dstChunk->Archetype->Offsets[indexInArchetype]; - var a = (int*) (dstChunk->Buffer + offset); - int count = dstChunk->Count; + int indexInArchetype = t + dstArchetype->FirstManagedComponent; + var offset = dstArchetype->Offsets[indexInArchetype]; + var a = (int*) (dstChunk.Buffer + offset); + int count = dstChunk.Count; if (hasCompanionComponents) { // We consider hybrid components as always different, there's no reason to clone those at this point. - var typeCategory = TypeManager.GetTypeInfo(dstChunk->Archetype->Types[indexInArchetype].TypeIndex).Category; + var typeCategory = TypeManager.GetTypeInfo(dstArchetype->Types[indexInArchetype].TypeIndex).Category; if (typeCategory == TypeManager.TypeCategory.UnityEngineObject) { // We still need to patch their indices, because otherwise they might point to invalid memory in diff --git a/Unity.Entities/Diff/EntityDifferEntityInChunkChanges.cs b/Unity.Entities/Diff/EntityDifferEntityInChunkChanges.cs index 309be57..2f86c7a 100644 --- a/Unity.Entities/Diff/EntityDifferEntityInChunkChanges.cs +++ b/Unity.Entities/Diff/EntityDifferEntityInChunkChanges.cs @@ -103,15 +103,16 @@ public void Execute(int index) var chunk = Chunks[index].m_Chunk; var flags = Flags[index]; var startIndex = EntityCounts[index]; + var chunkBuffer = chunk.Buffer; - var archetype = chunk->Archetype; + var archetype = Chunks[index].Archetype.Archetype; var entityGuidIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(archetype, EntityGuidTypeIndex); - var entityGuidBuffer = (EntityGuid*)(ChunkDataUtility.GetChunkBuffer(chunk) + archetype->Offsets[entityGuidIndexInArchetype]); + var entityGuidBuffer = (EntityGuid*)(chunkBuffer + archetype->Offsets[entityGuidIndexInArchetype]); var entityIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(archetype, EntityTypeIndex); - var entityBuffer = (Entity*)(ChunkDataUtility.GetChunkBuffer(chunk) + archetype->Offsets[entityIndexInArchetype]); + var entityBuffer = (Entity*)(chunkBuffer + archetype->Offsets[entityIndexInArchetype]); var entitiesIndex = startIndex; - for (var i = 0; i < chunk->Count; ++i) + for (int i = 0, count = chunk.Count; i < count; ++i) { var entityIndex = entityBuffer[i].Index; int nameIndex = 0; diff --git a/Unity.Entities/Diff/EntityPatcher.cs b/Unity.Entities/Diff/EntityPatcher.cs index eb311ff..ad4b3e4 100644 --- a/Unity.Entities/Diff/EntityPatcher.cs +++ b/Unity.Entities/Diff/EntityPatcher.cs @@ -5,9 +5,7 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; -#if !NET_DOTS using Unity.Properties; -#endif namespace Unity.Entities { @@ -483,19 +481,16 @@ internal static void ApplyCreateEntitiesWithArchetypes( in NativeParallelMultiHashMap packedEntities, in NativeArray createdArchetypes) { - var linkedEntityGroupTypeIndex = TypeManager.GetTypeIndex(); for (int i = 0; i < createdArchetypes.Length; i++) { - bool hasLinkedEntityGroup = false; var filteredArchetype = createdArchetypes[i]; + // Convert ComponentType Indices to ComponentTypes and create a ComponentTypeSet var typeSet = new NativeArray(filteredArchetype.TypeIndices.Length, Allocator.Temp); for (int j = 0; j < typeSet.Length; j++) { typeSet[j] = ComponentType.ReadOnly(filteredArchetype.TypeIndices[j]); - if (typeSet[j].TypeIndex == linkedEntityGroupTypeIndex) - hasLinkedEntityGroup = true; } // Create the archetype @@ -509,15 +504,6 @@ internal static void ApplyCreateEntitiesWithArchetypes( // PackedEntityIndices stores the indices of the Entities that have this archetype in the PackedTypes // By adding the Entity to that index, it lines up with the components in PackedTypes used in ApplyAddComponents packedEntities.Add(filteredArchetype.PackedEntityIndices[j], entities[j]); - - // By convention, the first entity in a LinkedEntityGroup dynamic buffer is the entity that owns it. - // This convention is relied on later in the patching process by BuildPrefabAndLinkedEntityGroupLookups - // and in particular the BuildLinkedEntityGroupHashMap job. - if (hasLinkedEntityGroup) - { - var buffer = entityManager.GetBuffer(entities[j]); - buffer.Add(new LinkedEntityGroup() {Value = entities[j]}); - } } } } @@ -1435,12 +1421,6 @@ internal static void ApplyLinkedEntityGroupAdditions( { do { - if (rootEntity == prefabEntityToInstantiate) - { - Debug.LogWarning($"Trying to instantiate self as child?"); - continue; - } - if (entityManager.HasComponent(rootEntity)) { entityManager.GetBuffer(rootEntity).Add(prefabEntityToInstantiate); @@ -1501,16 +1481,6 @@ internal static void ApplyLinkedEntityGroupAdditions( Debug.LogWarning("Failed to add a linked child. Multiple instances of the child entity were found in the destination world."); continue; } - - if (rootEntity == childEntityToLink) - { - // While this is actually valid and the intended way to use LinkedEntityGroup. - // We should never receive change set with this change. - // Instead we automatically add the root when adding the LinkedEntityGroup component. - Debug.LogWarning("Failed to add a linked child. Unable to link the root as a child."); - continue; - } - entityManager.GetBuffer(rootEntity).Add(childEntityToLink); entityToLinkedEntityGroupRoot.TryAdd(childEntityToLink, rootEntity); } diff --git a/Unity.Entities/Diff/EntityPatcherBlobs.cs b/Unity.Entities/Diff/EntityPatcherBlobs.cs index 45774b0..f97a7f8 100644 --- a/Unity.Entities/Diff/EntityPatcherBlobs.cs +++ b/Unity.Entities/Diff/EntityPatcherBlobs.cs @@ -1,8 +1,6 @@ using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -#if !NET_DOTS using Unity.Properties; -#endif namespace Unity.Entities { diff --git a/Unity.Entities/EntityBatchInChunk.cs b/Unity.Entities/EntityBatchInChunk.cs index 41ba35b..ba46b88 100644 --- a/Unity.Entities/EntityBatchInChunk.cs +++ b/Unity.Entities/EntityBatchInChunk.cs @@ -1,15 +1,10 @@ using System; -using Unity.Burst; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Jobs; -using UnityEngine.Profiling; namespace Unity.Entities { - internal unsafe struct EntityBatchInChunk + internal struct EntityBatchInChunk { - public Chunk* Chunk; + public ChunkIndex Chunk; public int StartIndex; public int Count; } diff --git a/Unity.Entities/EntityCommandBuffer.cs b/Unity.Entities/EntityCommandBuffer.cs index de1ba24..95c94d8 100644 --- a/Unity.Entities/EntityCommandBuffer.cs +++ b/Unity.Entities/EntityCommandBuffer.cs @@ -205,6 +205,14 @@ internal object GetBoxedObject() } } + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct EntityMoveManagedComponentCommand + { + public EntityCommand Header; + public Entity SrcEntity; + public TypeIndex ComponentTypeIndex; + } + [StructLayout(LayoutKind.Sequential)] internal unsafe struct EntityUnmanagedSharedComponentCommand { @@ -436,6 +444,7 @@ internal enum ECBCommand AddManagedComponentData, SetManagedComponentData, + MoveManagedComponentData, AddComponentLinkedEntityGroup, SetComponentLinkedEntityGroup, @@ -514,28 +523,18 @@ internal int BaseSortKey internal unsafe struct EntityCommandBufferData { public EntityCommandBufferChain m_MainThreadChain; - public EntityCommandBufferChain* m_ThreadedChains; - public int m_RecordedChainCount; - public int m_MinimumChunkSize; - public AllocatorManager.AllocatorHandle m_Allocator; - public PlaybackPolicy m_PlaybackPolicy; - public bool m_ShouldPlayback; - public bool m_DidPlayback; - + public bool m_ForceFullDisposeOnSkippedPlayback; public Entity m_Entity; - public int m_BufferWithFixupsCount; public UnsafeAtomicCounter32 m_BufferWithFixups; - private static readonly int ALIGN_64_BIT = 8; - public int m_CommandBufferID; internal void InitForParallelWriter() @@ -929,8 +928,16 @@ internal void AddEntityNameCommand(EntityCommandBufferChain* chain, int sortKey, BufferHeader* header = &cmd->BufferNode.TempBuffer; BufferHeader.Initialize(header, type.BufferCapacity); + // Track all DynamicBuffer headers created during recording. Until the ECB is played back, it owns the + // memory allocations for these buffers and is responsible for deallocating them when the ECB is disposed. cmd->BufferNode.Prev = chain->m_Cleanup->BufferCleanupList; chain->m_Cleanup->BufferCleanupList = &(cmd->BufferNode); + // The caller may invoke methods on the DynamicBuffer returned by this command during ECB recording which + // cause it to allocate memory (for example, DynamicBuffer.AddRange). These allocations always use + // Allocator.Persistent, not the ECB's allocator. These allocations must ALWAYS be manually cleaned up + // if the ECB is disposed without being played back. So, we have to force the full ECB cleanup process + // to run in this case, even if it could normally be skipped. + m_ForceFullDisposeOnSkippedPlayback = true; internalCapacity = type.BufferCapacity; @@ -1814,11 +1821,9 @@ internal void WaitForWriterJobs() private static readonly SharedStatic s_staticSafetyId = SharedStatic.GetOrCreate(); #endif -#if !UNITY_DOTSRUNTIME // TODO(michalb): bugfix for https://jira.unity3d.com/browse/BUR-1767, remove when burst is upgraded to 1.7.2. static readonly ProfilerMarker k_ProfileEcbPlayback = new ProfilerMarker("EntityCommandBuffer.Playback"); static readonly ProfilerMarker k_ProfileEcbDispose = new ProfilerMarker("EntityCommandBuffer.Dispose"); -#endif /// /// Allows controlling the size of chunks allocated from the temp job allocator to back the command buffer. /// @@ -1949,9 +1954,7 @@ public EntityCommandBuffer(AllocatorManager.AllocatorHandle allocator, PlaybackP [BurstCompile] static void DisposeInternal(ref EntityCommandBuffer ecb) { -#if !UNITY_DOTSRUNTIME k_ProfileEcbDispose.Begin(); -#endif #if ENABLE_UNITY_COLLECTIONS_CHECKS CollectionHelper.DisposeSafetyHandle(ref ecb.m_Safety0); AtomicSafetyHandle.Release(ecb.m_ArrayInvalidationSafety); @@ -1960,7 +1963,12 @@ static void DisposeInternal(ref EntityCommandBuffer ecb) // There's no need to walk chains and dispose individual allocations if the provided allocator // uses auto-dispose; they'll all be freed automatically when the allocator rewinds. - if (ecb.m_Data != null && !ecb.m_Data->m_Allocator.IsAutoDispose) + bool disposeChains = !ecb.m_Data->m_Allocator.IsAutoDispose; + // ...however, under some conditions we need to walk the chains anyway and manually free their allocations, + // even with auto-dispose allocators. + if (!disposeChains && !ecb.m_Data->m_DidPlayback && ecb.m_Data->m_ForceFullDisposeOnSkippedPlayback) + disposeChains = true; + if (ecb.m_Data != null && disposeChains) { ecb.FreeChain(&ecb.m_Data->m_MainThreadChain, ecb.m_Data->m_PlaybackPolicy, ecb.m_Data->m_DidPlayback); @@ -1982,9 +1990,7 @@ static void DisposeInternal(ref EntityCommandBuffer ecb) Memory.Unmanaged.Free(ecb.m_Data, ecb.m_Data->m_Allocator); } ecb.m_Data = null; -#if !UNITY_DOTSRUNTIME k_ProfileEcbDispose.End(); -#endif } /// @@ -3432,14 +3438,13 @@ internal void UnsafeSetSharedComponentManagedNonDefault(Entity e, object sharedC else { byte* componentAddr = null; + GCHandle handle = default; if (sharedComponent != null) { -#if !UNITY_DOTSRUNTIME - componentAddr = (byte*)UnsafeUtility.PinGCObjectAndGetAddress(sharedComponent, out var gcHandle) + TypeManager.ObjectOffset; -#else - throw new NotSupportedException("This API is not supported when called with unmanaged shared component on DOTS Runtime"); -#endif + handle = GCHandle.Alloc(sharedComponent, GCHandleType.Pinned); + componentAddr = (byte*)handle.AddrOfPinnedObject(); } + m_Data->AddEntityUnmanagedSharedComponentCommand( &m_Data->m_MainThreadChain, MainThreadSortKey, @@ -3450,6 +3455,11 @@ internal void UnsafeSetSharedComponentManagedNonDefault(Entity e, object sharedC TypeManager.GetTypeInfo(typeIndex).TypeSize, componentAddr); + if(componentAddr != null) + { + handle.Free(); + } + } } @@ -3669,9 +3679,7 @@ void PlaybackInternal(EntityDataAccess* mgr) AtomicSafetyHandle.CheckWriteAndBumpSecondaryVersion(m_ArrayInvalidationSafety); #endif -#if !UNITY_DOTSRUNTIME k_ProfileEcbPlayback.Begin(); -#endif if (ENABLE_PRE_PLAYBACK_VALIDATION) { @@ -3706,9 +3714,7 @@ void PlaybackInternal(EntityDataAccess* mgr) } m_Data->m_DidPlayback = true; -#if !UNITY_DOTSRUNTIME k_ProfileEcbPlayback.End(); -#endif } // This enum is used by the ECBInterop to allow us to have generic chain walking code @@ -3757,6 +3763,7 @@ internal interface IEcbProcessor public void SetComponentLinkedEntityGroup(BasicCommand* header); public void ReplaceComponentLinkedEntityGroup(BasicCommand* header); public void AddManagedComponentData(BasicCommand* header); + public void MoveManagedComponentData(BasicCommand* header); public void AddComponentObjectForMultipleEntities(BasicCommand* header); public void SetComponentObjectForMultipleEntities(BasicCommand* header); public void AddSharedComponentData(BasicCommand* header); @@ -3784,6 +3791,10 @@ internal static void ProcessManagedCommand(T* processor, BasicCommand* header processor->AddManagedComponentData(header); break; + case ECBCommand.MoveManagedComponentData: + processor->MoveManagedComponentData(header); + break; + case ECBCommand.AddSharedComponentData: processor->AddSharedComponentData(header); break; @@ -4771,15 +4782,25 @@ public void AddManagedComponentData(BasicCommand* header) } var box = cmd->GetBoxedObject(); -#if !NET_DOTS if (box != null && TypeManager.HasEntityReferences(cmd->ComponentTypeIndex)) FixupManagedComponent.FixUpComponent(box, playbackState); -#endif mgr->SetComponentObject(entity, ComponentType.FromTypeIndex(cmd->ComponentTypeIndex), box, in originSystem); } + [BurstDiscard] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void MoveManagedComponentData(BasicCommand* header) + { + var cmd = (EntityMoveManagedComponentCommand*)header; + var srcEntity = SelectEntity(cmd->SrcEntity, playbackState); + var dstEntity = SelectEntity(cmd->Header.Entity, playbackState); + var componentType = ComponentType.FromTypeIndex(cmd->ComponentTypeIndex); + mgr->MoveComponentObjectDuringStructuralChange(srcEntity, dstEntity, componentType, originSystem); + CommitStructuralChanges(mgr, ref archetypeChanges); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddUnmanagedSharedComponentData(BasicCommand* header) { @@ -4843,10 +4864,8 @@ public void AddComponentObjectForMultipleEntities(BasicCommand* header) var box = cmd->GetBoxedObject(); var typeIndex = cmd->ComponentTypeIndex; -#if !NET_DOTS if (box != null && TypeManager.HasEntityReferences(typeIndex)) FixupManagedComponent.FixUpComponent(box, playbackState); -#endif for (int len = entities.Length, i = 0; i < len; i++) { @@ -4878,10 +4897,8 @@ public void SetComponentObjectForMultipleEntities(BasicCommand* header) var box = cmd->GetBoxedObject(); var typeIndex = cmd->ComponentTypeIndex; -#if !NET_DOTS if (box != null && TypeManager.HasEntityReferences(typeIndex)) FixupManagedComponent.FixUpComponent(box, playbackState); -#endif for (int len = entities.Length, i = 0; i < len; i++) { @@ -5008,10 +5025,8 @@ public void SetManagedComponentData(BasicCommand* header) } var box = cmd->GetBoxedObject(); -#if !NET_DOTS if (box != null && TypeManager.HasEntityReferences(cmd->ComponentTypeIndex)) FixupManagedComponent.FixUpComponent(box, playbackState); -#endif mgr->SetComponentObject(entity, ComponentType.FromTypeIndex(cmd->ComponentTypeIndex), cmd->GetBoxedObject(), in originSystem); } @@ -5220,7 +5235,6 @@ private static void FixupComponentData(byte* data, int count, TypeIndex typeInde } } -#if !NET_DOTS class FixupManagedComponent : Unity.Properties.PropertyVisitor, Unity.Properties.IVisitPropertyAdapter { [ThreadStatic] @@ -5252,7 +5266,6 @@ void Unity.Properties.IVisitPropertyAdapter.Visit(in Unity.P } } } -#endif static void SetCommandDataWithFixup( EntityComponentStore* mgr, EntityComponentCommand* cmd, Entity entity, @@ -6435,6 +6448,42 @@ public static void AddComponent(this EntityCommandBuffer ecb, Entity e) where AddEntityManagedComponentCommandFromMainThread(ecb.m_Data, ecb.MainThreadSortKey, ECBCommand.AddManagedComponentData, e, default(T)); } + /// Records a command to safely move a managed component (and its current value) from one entity to another + /// At playback, this command throws an error if this entity is destroyed before playback, + /// if this entity is still deferred, or adding this componentType makes the archetype too large. + /// This entity command buffer. + /// The entity whose component should be moved to . This entity must have a component of type at playback time. + /// + /// The Entity the managed component will be added to. If this entity already has + /// with a different value than , the existing value will be + /// removed and disposed before the new value is assigned. + /// + /// The type of component to move. Must be a managed type. + /// + /// If the source and destination entity are identical, no operation is performed. + /// + /// This operation seems similar to + /// + /// value = GetComponentData<T>(src); + /// AddComponentData(dst, value) + /// RemoveComponent<T>(src) + /// + /// But for managed components which implement , calling RemoveComponent will invoke Dispose() on the component value, leaving the destination entity with an uninitialized object.``` + /// This operation ensures the component is properly moved over. + /// + /// Throws if an Allocator was not passed in when the EntityCommandBuffer was created. + /// Throws if this EntityCommandBuffer has already been played back. + /// Throws if does not have component at playback time. + [SupportedInEntitiesForEach] + public static void MoveComponent(this EntityCommandBuffer ecb, Entity src, Entity dst) where T : class, IComponentData, new() + { + if (src == dst) + return; + ecb.EnforceSingleThreadOwnership(); + ecb.AssertDidNotPlayback(); + AddMoveManagedComponentCommandFromMainThread(ecb.m_Data, ecb.MainThreadSortKey, ECBCommand.MoveManagedComponentData, src, dst); + } + /// Records a command to set a managed component for an entity. /// At playback, this command throws an error if this entity is destroyed before playback, /// if this entity is still deferred, if the entity has the tag, or if the entity doesn't have the shared component type. @@ -6533,6 +6582,10 @@ public static void AddComponent(this EntityCommandBuffer ecb, Entity e) where internal static void AddEntityManagedComponentCommandFromMainThread(EntityCommandBufferData* ecbd, int sortKey, ECBCommand op, Entity e, T component) where T : class { +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(e == Entity.Null)) + throw new InvalidOperationException("Invalid Entity.Null passed."); +#endif var typeIndex = TypeManager.GetTypeIndex(); var sizeNeeded = EntityCommandBufferData.Align(sizeof(EntityManagedComponentCommand), 8); @@ -6560,6 +6613,29 @@ internal static void AddEntityManagedComponentCommandFromMainThread(EntityCom data->GCNode.BoxedObject = new GCHandle(); } } + + internal static void AddMoveManagedComponentCommandFromMainThread(EntityCommandBufferData* ecbd, int sortKey, ECBCommand op, Entity src, Entity dst) where T : class + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(src == Entity.Null || dst == Entity.Null)) + throw new InvalidOperationException("Invalid Entity.Null passed."); +#endif + var typeIndex = TypeManager.GetTypeIndex(); + var sizeNeeded = EntityCommandBufferData.Align(sizeof(EntityMoveManagedComponentCommand), 8); + + var chain = &ecbd->m_MainThreadChain; + ecbd->ResetCommandBatching(chain); + var data = (EntityMoveManagedComponentCommand*)ecbd->Reserve(chain, sortKey, sizeNeeded); + + data->Header.Header.CommandType = op; + data->Header.Header.TotalSize = sizeNeeded; + data->Header.Header.SortKey = chain->m_LastSortKey; + data->Header.Entity = dst; + data->Header.IdentityIndex = 0; + data->Header.BatchCount = 1; + data->ComponentTypeIndex = typeIndex; + data->SrcEntity = src; + } } #endif } diff --git a/Unity.Entities/EntityCommandBufferDebug.cs b/Unity.Entities/EntityCommandBufferDebug.cs index 9b1c0c7..6272b60 100644 --- a/Unity.Entities/EntityCommandBufferDebug.cs +++ b/Unity.Entities/EntityCommandBufferDebug.cs @@ -493,6 +493,15 @@ public void AddManagedComponentData(BasicCommand* header) playbackProcessor.AddManagedComponentData(header); } + [BurstDiscard] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void MoveManagedComponentData(BasicCommand* header) + { + var cmd = (EntityMoveManagedComponentCommand*)header; + FixedString64Bytes commandAction = "Moving managed"; + LogEntityAndComponentCommand(cmd->Header.Entity, cmd->ComponentTypeIndex, in commandAction); + playbackProcessor.MoveManagedComponentData(header); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddUnmanagedSharedComponentData(BasicCommand* header) @@ -1112,6 +1121,19 @@ public void AddManagedComponentData(BasicCommand* header) playbackProcessor.AddManagedComponentData(header); } + [BurstDiscard] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void MoveManagedComponentData(BasicCommand* header) + { + var cmd = (EntityMoveManagedComponentCommand*)header; + + var dstEntity = SelectEntity(cmd->Header.Entity, playbackProcessor.playbackState); + ThrowIfPrefab(dstEntity); + var srcEntity = SelectEntity(cmd->SrcEntity, playbackProcessor.playbackState); + ThrowIfPrefab(srcEntity); + + playbackProcessor.MoveManagedComponentData(header); + } [MethodImpl(MethodImplOptions.AggressiveInlining)] public void AddUnmanagedSharedComponentData(BasicCommand* header) @@ -1464,6 +1486,11 @@ public unsafe void AddManagedComponentData(BasicCommand* header) commands.Add(header); } + public unsafe void MoveManagedComponentData(BasicCommand* header) + { + commands.Add(header); + } + public unsafe void AddSharedComponentData(BasicCommand* header) { commands.Add(header); @@ -1660,12 +1687,8 @@ public EntityQueryComponentCommandView(ECBCommand commandType, int sortKey, int public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; -#else - var typeName = ""; -#endif return (CommandType == ECBCommand.AddComponentForEntityQuery) ? $"Add {typeName}Component to EntityQuery" : $"Remove {typeName}Component from EntityQuery"; } } @@ -1754,12 +1777,8 @@ public MultipleEntitiesComponentCommandView(ECBCommand commandType, int sortKey, public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; -#else - var typeName = ""; -#endif return (CommandType == ECBCommand.AddComponentForMultipleEntities) ? $"Add {typeName}Component to {EntitiesCount} Entities" : $"Remove {typeName}Component from {EntitiesCount} Entities"; } } @@ -1794,12 +1813,8 @@ public MultipleEntitiesComponentCommandWithObjectView(ECBCommand commandType, in public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; -#else - var typeName = ""; -#endif return CommandType == ECBCommand.AddComponentObjectForMultipleEntities || CommandType == ECBCommand.AddSharedComponentWithValueForMultipleEntities ? $"Add {typeName}Component to {EntitiesCount} Entities" : @@ -1833,12 +1848,8 @@ public EntityQueryComponentCommandWithObjectView(ECBCommand commandType, int sor public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; -#else - var typeName = ""; -#endif return CommandType == ECBCommand.AddSharedComponentWithValueForEntityQuery || CommandType == ECBCommand.AddComponentObjectForEntityQuery ? $"Add {typeName}Component to EntityQuery" : @@ -1906,12 +1917,8 @@ public EntityComponentCommandView(ECBCommand commandType, int sortKey, int total public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; - #else - var typeName = ""; -#endif switch (CommandType) { case ECBCommand.RemoveComponent: return $"Remove {typeName}Component"; @@ -1963,12 +1970,8 @@ public EntityQueryMaskCommandView(ECBCommand commandType, int sortKey, int total public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; - #else - var typeName = ""; -#endif switch (CommandType) { case ECBCommand.AddComponentLinkedEntityGroup: return $"Add {typeName}Component for LinkedEntityGroup"; @@ -2020,13 +2023,8 @@ public EntityComponentEnabledCommandView(ECBCommand commandType, int sortKey, in public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; -#else - var typeName = ""; -#endif - return IsEnabled != 1 ? $"{typeName}Component Enabled" : $"{typeName}Component Disabled"; } @@ -2103,12 +2101,8 @@ public EntityBufferCommandView(ECBCommand commandType, int sortKey, int totalSiz public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = " " + type.Name; -#else - var typeName = ""; -#endif return CommandType == ECBCommand.AddBuffer || CommandType == ECBCommand.AddBufferWithEntityFixUp ? $"Add Entity Buffer{typeName}" : $"Set Entity Buffer{typeName}"; } } @@ -2140,17 +2134,38 @@ public EntityManagedComponentCommandView(ECBCommand commandType, int sortKey, in public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; -#else - var typeName = ""; -#endif - return CommandType == ECBCommand.AddManagedComponentData ? $"Add {typeName}Component (Managed)" : $"Set {typeName}Component (Managed)"; } } + internal class EntityMoveManagedComponentCommandView : EntityCommandView + { + public TypeIndex ComponentTypeIndex; + public Entity SrcEntity; + + public EntityMoveManagedComponentCommandView(ECBCommand commandType, int sortKey, int totalSize, Entity srcEntity, + Entity dstEntity, int identityIndex, int batchCount, TypeIndex componentTypeIndex) + { + CommandType = commandType; + SortKey = sortKey; + TotalSizeInBytes = totalSize; + Entity = dstEntity; + IdentityIndex = identityIndex; + BatchCount = batchCount; + ComponentTypeIndex = componentTypeIndex; + SrcEntity = srcEntity; + } + + public override string ToString() + { + var type = TypeManager.GetType(ComponentTypeIndex); + var typeName = type.Name + " "; + return $"Move {typeName}Component (Managed)"; + } + } + internal class EntityUnmanagedSharedComponentCommandView : EntityCommandView { public TypeIndex ComponentTypeIndex; @@ -2184,13 +2199,8 @@ public EntityUnmanagedSharedComponentCommandView( public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; -#else - var typeName = ""; -#endif - return CommandType == ECBCommand.AddUnmanagedSharedComponentData ? $"Add {typeName}UnmanagedSharedComponentData" : $"Set {typeName}UnmanagedSharedComponentData"; } } @@ -2230,12 +2240,8 @@ public MultipleEntitiesComponentCommandView_WithUnmanagedSharedValue( public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; -#else - var typeName = ""; -#endif return (CommandType == ECBCommand.AddUnmanagedSharedComponentValueForMultipleEntities) ? $"Add {typeName}Unmanaged Shared Component to {EntitiesCount} Entities" : $"Set {typeName}Unmanaged Shared Component from {EntitiesCount} Entities"; @@ -2270,12 +2276,8 @@ public EntityQueryComponentCommandView_WithUnmanagedSharedValue( public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; -#else - var typeName = ""; -#endif return (CommandType == ECBCommand.AddUnmanagedSharedComponentValueForEntityQuery) ? $"Add {typeName}Unmanaged Shared Component to EntityQuery" : $"Set {typeName}Unmanaged Shared Component from EntityQuery"; @@ -2311,20 +2313,14 @@ public EntitySharedComponentCommandView(ECBCommand commandType, int sortKey, int public override string ToString() { -#if !NET_DOTS var type = TypeManager.GetType(ComponentTypeIndex); var typeName = type.Name + " "; -#else - var typeName = ""; -#endif - return CommandType == ECBCommand.AddSharedComponentData ? $"Add {typeName}SharedComponentData" : $"Set {typeName}SharedComponentData"; } } internal sealed class EntityCommandBufferDebugView { -#if !NET_DOTS private EntityCommandBuffer m_ecb; public EntityCommandBufferDebugView(EntityCommandBuffer ecb) { @@ -2485,9 +2481,17 @@ public BasicCommandView ProcessDebugViewCommand(BasicCommand* header) entityManagedComponentCommand->Header.BatchCount, entityManagedComponentCommand->ComponentTypeIndex, entityManagedComponentCommand->GCNode); + case ECBCommand.MoveManagedComponentData: + var entityMoveManagedComponentCommand = (EntityMoveManagedComponentCommand*) header; + return new EntityMoveManagedComponentCommandView(header->CommandType, + header->SortKey, header->TotalSize, entityMoveManagedComponentCommand->SrcEntity, + entityMoveManagedComponentCommand->Header.Entity, + entityMoveManagedComponentCommand->Header.IdentityIndex, + entityMoveManagedComponentCommand->Header.BatchCount, + entityMoveManagedComponentCommand->ComponentTypeIndex); + case ECBCommand.AddSharedComponentData: case ECBCommand.SetSharedComponentData: - var entitySharedComponentCommand = (EntitySharedComponentCommand*) header; return new EntitySharedComponentCommandView(header->CommandType, header->SortKey, header->TotalSize, entitySharedComponentCommand->Header.Entity, @@ -2584,7 +2588,6 @@ public BasicCommandView[] Commands return commandViewArray; } } -#endif } } } diff --git a/Unity.Entities/EntityCommandBufferSystem.cs b/Unity.Entities/EntityCommandBufferSystem.cs index d9121b9..4367033 100644 --- a/Unity.Entities/EntityCommandBufferSystem.cs +++ b/Unity.Entities/EntityCommandBufferSystem.cs @@ -154,7 +154,7 @@ protected override void OnCreate() } /// - /// Destroys this system, executing any pending command buffers first. + /// Destroys this system. Any pending command buffers will be disposed, but their commands will NOT be played back. /// /// If you override this method, you should call `base.OnDestroy()` to retain the default /// destruction logic. @@ -272,21 +272,11 @@ internal void FlushPendingBuffers(bool playBack) #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG if (playbackErrorLog != null) { -#if !NET_DOTS var exceptionMessage = new StringBuilder(); foreach (var err in playbackErrorLog) { exceptionMessage.AppendLine(err); } -#else - var exceptionMessage = ""; - foreach (var err in playbackErrorLog) - { - exceptionMessage += err; - exceptionMessage += '\n'; - } -#endif - throw new ArgumentException(exceptionMessage.ToString()); } #endif diff --git a/Unity.Entities/EntityComponentStore.cs b/Unity.Entities/EntityComponentStore.cs index 4ffca00..c8e3b5d 100644 --- a/Unity.Entities/EntityComponentStore.cs +++ b/Unity.Entities/EntityComponentStore.cs @@ -11,6 +11,7 @@ using System.Runtime.InteropServices; using System.Collections.Generic; using Unity.Burst.CompilerServices; +using Unity.Entities.LowLevel; // --------------------------------------------------------------------------------------------------------- // EntityComponentStore @@ -130,7 +131,7 @@ public unsafe void IncrementComponentOrderVersion(Archetype* archetype, } } - public void PatchEntities(Archetype* archetype, Chunk* chunk, int entityCount, + public void PatchEntities(Archetype* archetype, ChunkIndex chunk, int entityCount, NativeArray remapping) { // In every case this is called ManagedChangesTracker.Playback() is called in the same calling function. @@ -138,12 +139,12 @@ public void PatchEntities(Archetype* archetype, Chunk* chunk, int entityCount, CommandBuffer.Add((int)Command.PatchManagedEntities); CommandBuffer.Add((IntPtr)archetype); - CommandBuffer.Add((IntPtr)chunk); + CommandBuffer.Add(chunk); CommandBuffer.Add(entityCount); CommandBuffer.Add((IntPtr)remapping.GetUnsafePtr()); } - public void PatchEntitiesForPrefab(Archetype* archetype, Chunk* chunk, int indexInChunk, int allocatedCount, + public void PatchEntitiesForPrefab(Archetype* archetype, ChunkIndex chunk, int indexInChunk, int allocatedCount, Entity* remapSrc, Entity* remapDst, int remappingCount, AllocatorManager.AllocatorHandle allocator) { // We are deferring the patching so we need a copy of the remapping info since we can't be certain of its lifetime. @@ -169,7 +170,7 @@ public void PatchEntitiesForPrefab(Archetype* archetype, Chunk* chunk, int index if (archetype->Types[indexInArchetype].HasEntityReferences) { - var a = (int*)ChunkDataUtility.GetComponentDataRO(chunk, 0, indexInArchetype); + var a = (int*)ChunkDataUtility.GetComponentDataRO(chunk, archetype, 0, indexInArchetype); for (int ei = 0; ei < allocatedCount; ++ei) managedComponents[ei * numManagedComponents + i] = a[ei + indexInChunk]; } @@ -382,9 +383,6 @@ internal unsafe partial struct EntityComponentStore [NativeDisableUnsafePtrRestriction] int* m_VersionByEntity; - [NativeDisableUnsafePtrRestriction] - Archetype** m_ArchetypeByEntity; - [NativeDisableUnsafePtrRestriction] EntityInChunk* m_EntityInChunkByEntity; @@ -576,8 +574,6 @@ void IncreaseCapacity() void ResizeUnmanagedArrays(long oldValue, long newValue) { m_VersionByEntity = Memory.Unmanaged.Array.Resize(m_VersionByEntity, oldValue, newValue, Allocator.Persistent); - IntPtr* temp = (IntPtr*) m_ArchetypeByEntity; - m_ArchetypeByEntity = (Archetype**) Memory.Unmanaged.Array.Resize(temp, oldValue, newValue, Allocator.Persistent); m_EntityInChunkByEntity = Memory.Unmanaged.Array.Resize(m_EntityInChunkByEntity, oldValue, newValue, Allocator.Persistent); #if !DOTS_DISABLE_DEBUG_NAMES m_NameByEntity = Memory.Unmanaged.Array.Resize(m_NameByEntity, oldValue, newValue, Allocator.Persistent); @@ -639,13 +635,13 @@ public Entity GetEntityByEntityIndex(int index) return new Entity(); } - private void InitializeAdditionalCapacity(int start) + void InitializeAdditionalCapacity(int start) { for (var i = start; i != EntitiesCapacity; i++) { m_EntityInChunkByEntity[i].IndexInChunk = i + 1; m_VersionByEntity[i] = 1; - m_EntityInChunkByEntity[i].Chunk = null; + m_EntityInChunkByEntity[i].Chunk = new ChunkIndex(); #if !DOTS_DISABLE_DEBUG_NAMES m_NameByEntity[i] = new EntityName(); #endif @@ -662,6 +658,9 @@ private void InitializeAdditionalCapacity(int start) public static void Create(EntityComponentStore* entities, ulong worldSequenceNumber, int newCapacity = kDefaultCapacity) { + PerChunkArray.Initialize(); + ChunkStore.Initialize(); + UnsafeUtility.MemClear(entities, sizeof(EntityComponentStore)); #if !DOTS_DISABLE_DEBUG_NAMES @@ -760,7 +759,6 @@ void Dispose() ResizeUnmanagedArrays(m_EntitiesCapacity, 0); m_VersionByEntity = null; - m_ArchetypeByEntity = null; m_EntityInChunkByEntity = null; #if !DOTS_DISABLE_DEBUG_NAMES m_NameByEntity = null; @@ -784,7 +782,7 @@ void Dispose() { var chunk = archetype->Chunks[c]; - ChunkDataUtility.DeallocateBuffers(chunk); + ChunkDataUtility.DeallocateBuffers(archetype, chunk); s_chunkStore.Data.FreeContiguousChunks(archetype->Chunks[c], 1); } @@ -800,9 +798,7 @@ void Dispose() m_ArchetypeChunkAllocator.Dispose(); ManagedChangesTracker.Dispose(); m_ManagedComponentFreeIndex.Dispose(); -#if !UNITY_DOTSRUNTIME AspectTypeInfoManager.Dispose(); -#endif for (int i = 0; i < m_UnmanagedSharedComponentsByType.Length; i++) { @@ -898,7 +894,7 @@ public void FreeAllEntities(bool resetVersion) for (var i = 0; i != EntitiesCapacity; i++) { m_EntityInChunkByEntity[i].IndexInChunk = i + 1; - m_EntityInChunkByEntity[i].Chunk = null; + m_EntityInChunkByEntity[i].Chunk = ChunkIndex.Null; #if !DOTS_DISABLE_DEBUG_NAMES m_NameByEntity[i] = new EntityName(); m_NameChangeBitsByEntity.Set(i, false); @@ -916,25 +912,23 @@ public void FreeAllEntities(bool resetVersion) m_VersionByEntity[i] += 1; } - - // Last entity indexInChunk identifies that we ran out of space... m_EntityInChunkByEntity[EntitiesCapacity - 1].IndexInChunk = -1; m_NextFreeEntityIndex = 0; m_EntityCreateDestroyVersion++; } - public void FreeEntities(Chunk* chunk) + public void FreeEntities(ChunkIndex chunk) { - var count = chunk->Count; - var entities = (Entity*)chunk->Buffer; + var count = chunk.Count; + var entities = (Entity*)chunk.Buffer; int freeIndex = m_NextFreeEntityIndex; for (var i = 0; i != count; i++) { int index = entities[i].Index; m_VersionByEntity[index] += 1; - m_EntityInChunkByEntity[index].Chunk = null; + m_EntityInChunkByEntity[index].Chunk = ChunkIndex.Null; m_EntityInChunkByEntity[index].IndexInChunk = freeIndex; #if !DOTS_DISABLE_DEBUG_NAMES m_NameByEntity[index] = new EntityName(); @@ -1005,31 +999,70 @@ public void CopyName(Entity dstEntity, Entity srcEntity) public Archetype* GetArchetype(Entity entity) { - return m_ArchetypeByEntity[entity.Index]; + AssertEntitiesExist(&entity, 1); + var chunk = GetChunk(entity); + Assert.IsTrue(chunk != ChunkIndex.Null); + return GetArchetype(chunk); } - public void SetArchetype(Entity entity, Archetype* archetype) + public ChunkIndex GetChunk(Entity entity) { - m_ArchetypeByEntity[entity.Index] = archetype; + var entityChunk = m_EntityInChunkByEntity[entity.Index].Chunk; + + return entityChunk; } - public void SetArchetype(Chunk* srcChunk, Archetype* dstArchetype) + [BurstCompile] + internal struct PerChunkArray { - var entities = (Entity*)srcChunk->Buffer; - var count = srcChunk->Count; - for (int i = 0; i < count; ++i) + public struct PerChunkData { - m_ArchetypeByEntity[entities[i].Index] = dstArchetype; + public Archetype* Archetype; + public int EntityCount; + public int ListIndex; } - srcChunk->Archetype = dstArchetype; + PerChunkData* m_PerChunkData; + + [BurstDiscard] + internal static void Initialize() + { + if (StaticIdentifier.Ref.Data.m_PerChunkData == null) + { + var size = sizeof(PerChunkData) * ChunkStore.kMaximumChunkCount; + var data = (PerChunkData*)Memory.Unmanaged.Allocate(size, CollectionHelper.CacheLineSize, Allocator.Persistent); + UnsafeUtility.MemClear(data, size); + StaticIdentifier.Ref.Data.m_PerChunkData = data; + + void Shutdown() + { + Memory.Unmanaged.Free(StaticIdentifier.Ref.Data.m_PerChunkData, Allocator.Persistent); + StaticIdentifier.Ref.Data.m_PerChunkData = null; + } + + AppDomain.CurrentDomain.DomainUnload += (_, _) => Shutdown(); + AppDomain.CurrentDomain.ProcessExit += (_, _) => Shutdown(); + } + } + + sealed class StaticIdentifier + { + internal static readonly SharedStatic Ref = SharedStatic.GetOrCreate(); + } + + public static PerChunkData* ChunkData => StaticIdentifier.Ref.Data.m_PerChunkData; } - public Chunk* GetChunk(Entity entity) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public Archetype* GetArchetype(ChunkIndex chunk) { - var entityChunk = m_EntityInChunkByEntity[entity.Index].Chunk; + return PerChunkArray.ChunkData[chunk].Archetype; + } - return entityChunk; + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void SetArchetype(ChunkIndex chunk, Archetype* archetype) + { + PerChunkArray.ChunkData[chunk].Archetype = archetype; } public void SetEntityInChunk(Entity entity, EntityInChunk entityInChunk) @@ -1057,19 +1090,6 @@ public void IncrementComponentTypeOrderVersion(Archetype* archetype) } } - public bool TryGetComponent(Entity entity, TypeIndex typeIndex, out EntityGuid entityGuid) - { - entityGuid = default; - - if (!HasComponent(entity, typeIndex)) - { - return false; - } - - entityGuid = *(EntityGuid*)GetComponentDataWithTypeRO(entity, typeIndex); - return true; - } - public bool Exists(Entity entity) { int index = entity.Index; @@ -1077,7 +1097,7 @@ public bool Exists(Entity entity) ValidateEntity(entity); var versionMatches = m_VersionByEntity[index] == entity.Version; - var hasChunk = m_EntityInChunkByEntity[index].Chunk != null; + var hasChunk = m_EntityInChunkByEntity[index].Chunk != ChunkIndex.Null; return versionMatches && hasChunk; } @@ -1086,11 +1106,11 @@ public static bool Debugger_Exists(EntityComponentStore* store, Entity entity) { int index = entity.Index; - if (store == null || index < 0 || index >= store->EntitiesCapacity || store->m_VersionByEntity == null || store->m_EntityInChunkByEntity == null || store->m_ArchetypeByEntity == null) + if (store == null || index < 0 || index >= store->EntitiesCapacity || store->m_VersionByEntity == null || store->m_EntityInChunkByEntity == null) return false; var versionMatches = store->m_VersionByEntity[index] == entity.Version; - var hasChunk = store->m_EntityInChunkByEntity[index].Chunk != null; + var hasChunk = store->m_EntityInChunkByEntity[index].Chunk != ChunkIndex.Null; return versionMatches && hasChunk; } @@ -1106,7 +1126,7 @@ public bool HasComponent(Entity entity, TypeIndex type) if (Hint.Unlikely(!Exists(entity))) return false; - var archetype = m_ArchetypeByEntity[entity.Index]; + var archetype = GetArchetype(entity); return ChunkDataUtility.GetIndexInTypeArray(archetype, type) != -1; } @@ -1115,7 +1135,7 @@ internal bool HasComponent(Entity entity, TypeIndex type, ref LookupCache cache) if (Hint.Unlikely(!Exists(entity))) return false; - var archetype = m_ArchetypeByEntity[entity.Index]; + var archetype = GetArchetype(entity); if (Hint.Unlikely(archetype != cache.Archetype)) cache.Update(archetype, type); return cache.IndexInArchetype != -1; @@ -1126,7 +1146,7 @@ public bool HasComponent(Entity entity, ComponentType type) if (Hint.Unlikely(!Exists(entity))) return false; - var archetype = m_ArchetypeByEntity[entity.Index]; + var archetype = GetArchetype(entity); return ChunkDataUtility.GetIndexInTypeArray(archetype, type.TypeIndex) != -1; } @@ -1149,13 +1169,13 @@ public void SetChunkComponent(ArchetypeChunk* chunks, int chunkCount, void* comp for (int i = 0; i < chunkCount; i++) { var srcChunk = chunks[i].m_Chunk; - var ptr = GetComponentDataWithTypeRW(srcChunk->metaChunkEntity, componentTypeIndex, m_GlobalSystemVersion); + var ptr = GetComponentDataWithTypeRW(srcChunk.MetaChunkEntity, componentTypeIndex, m_GlobalSystemVersion); var sizeInChunk = GetTypeInfo(componentTypeIndex).SizeInChunk; UnsafeUtility.MemCpy(ptr, componentData, sizeInChunk); } } - public void GetChunk(Entity entity, out Chunk* chunk, out int chunkIndex) + public void GetChunk(Entity entity, out ChunkIndex chunk, out int chunkIndex) { var entityChunk = m_EntityInChunkByEntity[entity.Index].Chunk; var entityIndexInChunk = m_EntityInChunkByEntity[entity.Index].IndexInChunk; @@ -1168,8 +1188,9 @@ public void GetChunk(Entity entity, out Chunk* chunk, out int chunkIndex) { var entityChunk = m_EntityInChunkByEntity[entity.Index].Chunk; var entityIndexInChunk = m_EntityInChunkByEntity[entity.Index].IndexInChunk; + var archetype = GetArchetype(entityChunk); - return ChunkDataUtility.GetComponentDataWithTypeRO(entityChunk, entityIndexInChunk, typeIndex); + return ChunkDataUtility.GetComponentDataWithTypeRO(entityChunk, archetype, entityIndexInChunk, typeIndex); } public static byte* Debugger_GetComponentDataWithTypeRO(EntityComponentStore* store, Entity entity, TypeIndex typeIndex) @@ -1178,22 +1199,24 @@ public void GetChunk(Entity entity, out Chunk* chunk, out int chunkIndex) return null; var entityChunk = store->m_EntityInChunkByEntity[entity.Index].Chunk; + var entityArchetype = store->GetArchetype(entityChunk); var entityIndexInChunk = store->m_EntityInChunkByEntity[entity.Index].IndexInChunk; - if (entityChunk == null && entityIndexInChunk < 0 || entityIndexInChunk > entityChunk->Count || entityChunk->Archetype == null) + if (entityChunk == ChunkIndex.Null && entityIndexInChunk < 0 || entityIndexInChunk > entityChunk.Count || entityArchetype == null) return null; - var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(entityChunk->Archetype, typeIndex); + var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(entityArchetype, typeIndex); if (indexInTypeArray == -1) return null; - return ChunkDataUtility.GetComponentDataRO(entityChunk, entityIndexInChunk, indexInTypeArray); + return ChunkDataUtility.GetComponentDataRO(entityChunk, entityArchetype, entityIndexInChunk, indexInTypeArray); } public byte* GetComponentDataWithTypeRW(Entity entity, TypeIndex typeIndex, uint globalVersion) { var entityChunk = m_EntityInChunkByEntity[entity.Index].Chunk; var entityIndexInChunk = m_EntityInChunkByEntity[entity.Index].IndexInChunk; + var archetype = GetArchetype(entityChunk); - var data = ChunkDataUtility.GetComponentDataWithTypeRW(entityChunk, entityIndexInChunk, typeIndex, + var data = ChunkDataUtility.GetComponentDataWithTypeRW(entityChunk, archetype, entityIndexInChunk, typeIndex, globalVersion); #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING @@ -1208,20 +1231,21 @@ public void GetChunk(Entity entity, out Chunk* chunk, out int chunkIndex) // responsibility to ensure that the type exists on the entity. public byte* GetComponentDataWithTypeRO(Entity entity, TypeIndex typeIndex, ref LookupCache cache) { - return ChunkDataUtility.GetComponentDataWithTypeRO(m_EntityInChunkByEntity[entity.Index].Chunk, m_ArchetypeByEntity[entity.Index], m_EntityInChunkByEntity[entity.Index].IndexInChunk, typeIndex, ref cache); + return ChunkDataUtility.GetComponentDataWithTypeRO(m_EntityInChunkByEntity[entity.Index].Chunk, GetArchetype(entity), m_EntityInChunkByEntity[entity.Index].IndexInChunk, typeIndex, ref cache); } // This method will return a null pointer if the entity does not have the provided type. public byte* GetOptionalComponentDataWithTypeRO(Entity entity, TypeIndex typeIndex, ref LookupCache cache) { - return ChunkDataUtility.GetOptionalComponentDataWithTypeRO(m_EntityInChunkByEntity[entity.Index].Chunk, m_ArchetypeByEntity[entity.Index], m_EntityInChunkByEntity[entity.Index].IndexInChunk, typeIndex, ref cache); + var entityInChunk = m_EntityInChunkByEntity[entity.Index]; + return ChunkDataUtility.GetOptionalComponentDataWithTypeRO(entityInChunk.Chunk, GetArchetype(entityInChunk.Chunk), entityInChunk.IndexInChunk, typeIndex, ref cache); } // This method will return an invalid pointer if the entity does not have the provided type. It is the caller's // responsibility to ensure that the type exists on the entity. public byte* GetComponentDataWithTypeRW(Entity entity, TypeIndex typeIndex, uint globalVersion, ref LookupCache cache) { - var data = ChunkDataUtility.GetComponentDataWithTypeRW(m_EntityInChunkByEntity[entity.Index].Chunk, m_ArchetypeByEntity[entity.Index], m_EntityInChunkByEntity[entity.Index].IndexInChunk, typeIndex, globalVersion, ref cache); + var data = ChunkDataUtility.GetComponentDataWithTypeRW(m_EntityInChunkByEntity[entity.Index].Chunk, GetArchetype(entity), m_EntityInChunkByEntity[entity.Index].IndexInChunk, typeIndex, globalVersion, ref cache); #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING if (Burst.CompilerServices.Hint.Unlikely(m_RecordToJournal != 0)) @@ -1234,7 +1258,7 @@ public void GetChunk(Entity entity, out Chunk* chunk, out int chunkIndex) // This method will return a null pointer if the entity does not have the provided type. public byte* GetOptionalComponentDataWithTypeRW(Entity entity, TypeIndex typeIndex, uint globalVersion, ref LookupCache cache) { - var data = ChunkDataUtility.GetOptionalComponentDataWithTypeRW(m_EntityInChunkByEntity[entity.Index].Chunk, m_ArchetypeByEntity[entity.Index], m_EntityInChunkByEntity[entity.Index].IndexInChunk, typeIndex, globalVersion, ref cache); + var data = ChunkDataUtility.GetOptionalComponentDataWithTypeRW(m_EntityInChunkByEntity[entity.Index].Chunk, GetArchetype(entity), m_EntityInChunkByEntity[entity.Index].IndexInChunk, typeIndex, globalVersion, ref cache); #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING if (data != null && Burst.CompilerServices.Hint.Unlikely(m_RecordToJournal != 0)) @@ -1279,49 +1303,46 @@ public void SetBufferRaw(Entity entity, TypeIndex componentTypeIndex, BufferHead [GenerateTestsForBurstCompatibility] public int GetSharedComponentDataIndex(Entity entity, TypeIndex typeIndex) { - var archetype = m_ArchetypeByEntity[entity.Index]; + var archetype = GetArchetype(entity); var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, typeIndex); var chunk = m_EntityInChunkByEntity[entity.Index].Chunk; - var sharedComponentValueArray = chunk->SharedComponentValues; + var sharedComponentValueArray = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); var sharedComponentOffset = indexInTypeArray - archetype->FirstSharedComponent; return sharedComponentValueArray[sharedComponentOffset]; } public int Debugger_GetSharedComponentDataIndex(Entity entity, TypeIndex typeIndex) { - var archetype = m_ArchetypeByEntity[entity.Index]; - if (archetype == null) - return -1; + var archetype = GetArchetype(entity); var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, typeIndex); if (indexInTypeArray == -1) return -1; var chunk = m_EntityInChunkByEntity[entity.Index].Chunk; - var sharedComponentValueArray = chunk->SharedComponentValues; + var sharedComponentValueArray = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); var sharedComponentOffset = indexInTypeArray - archetype->FirstSharedComponent; return sharedComponentValueArray[sharedComponentOffset]; } public void AllocateConsecutiveEntitiesForLoading(int count) { - int newCapacity = count + 1; // make room for Entity.Null // The last entity is used to indicate we ran out of space. // We need to also reset _all_ entities, not just the new ones because we are manually manipulating // the free list here by setting the next free entity index. - EnsureCapacity(newCapacity + 1, true); - m_NextFreeEntityIndex = newCapacity; + EnsureCapacity(count + 1, true); + m_NextFreeEntityIndex = count; m_EntityCreateDestroyVersion++; } - public void AddExistingEntitiesInChunk(Chunk* chunk) + public void AddExistingEntitiesInChunk(ChunkIndex chunk) { - for (int iEntity = 0; iEntity < chunk->Count; ++iEntity) + var archetype = GetArchetype(chunk); + for (int iEntity = 0, count = chunk.Count; iEntity < count; ++iEntity) { - var entity = (Entity*)ChunkDataUtility.GetComponentDataRO(chunk, iEntity, 0); + var entity = (Entity*)ChunkDataUtility.GetComponentDataRO(chunk, archetype, iEntity, 0); m_EntityInChunkByEntity[entity->Index].Chunk = chunk; m_EntityInChunkByEntity[entity->Index].IndexInChunk = iEntity; - m_ArchetypeByEntity[entity->Index] = chunk->Archetype; m_VersionByEntity[entity->Index] = entity->Version; } } @@ -1332,7 +1353,7 @@ public void AllocateEntitiesForRemapping(EntityComponentStore* srcEntityComponen for (var i = 0; i != count; i++) { - if (srcEntityComponentStore->m_EntityInChunkByEntity[i].Chunk != null) + if (srcEntityComponentStore->m_EntityInChunkByEntity[i].Chunk != ChunkIndex.Null) { var entityIndexInChunk = m_EntityInChunkByEntity[m_NextFreeEntityIndex].IndexInChunk; if (entityIndexInChunk == -1) @@ -1357,10 +1378,10 @@ public void AllocateEntitiesForRemapping(EntityComponentStore* srcEntityComponen } } - public void AllocateEntitiesForRemapping(Chunk* chunk, EntityComponentStore* srcComponentStore, ref NativeArray entityRemapping) + public void AllocateEntitiesForRemapping(ChunkIndex chunk, EntityComponentStore* srcComponentStore, ref NativeArray entityRemapping) { - var count = chunk->Count; - var entities = (Entity*)chunk->Buffer; + var count = chunk.Count; + var entities = (Entity*)chunk.Buffer; for (var i = 0; i != count; i++) { @@ -1385,12 +1406,9 @@ public void AllocateEntitiesForRemapping(Chunk* chunk, EntityComponentStore* src } } - public void RemapChunk(Archetype* arch, Chunk* chunk, int baseIndex, int count, ref NativeArray entityRemapping) + public void RemapChunk(ChunkIndex chunk, int baseIndex, int count, ref NativeArray entityRemapping) { - Assert.AreEqual(chunk->Archetype->Offsets[0], 0); - Assert.AreEqual(chunk->Archetype->SizeOfs[0], sizeof(Entity)); - - var entityInChunkStart = (Entity*)(chunk->Buffer) + baseIndex; + var entityInChunkStart = (Entity*)chunk.Buffer + baseIndex; for (var i = 0; i != count; i++) { @@ -1403,13 +1421,12 @@ public void RemapChunk(Archetype* arch, Chunk* chunk, int baseIndex, int count, entityInChunk->Index = target.Index; entityInChunk->Version = entityVersion; m_EntityInChunkByEntity[target.Index].IndexInChunk = baseIndex + i; - m_ArchetypeByEntity[target.Index] = arch; m_EntityInChunkByEntity[target.Index].Chunk = chunk; } - if (chunk->metaChunkEntity != Entity.Null) + if (chunk.MetaChunkEntity != Entity.Null) { - chunk->metaChunkEntity = EntityRemapUtility.RemapEntity(ref entityRemapping, chunk->metaChunkEntity); + chunk.MetaChunkEntity = EntityRemapUtility.RemapEntity(ref entityRemapping, chunk.MetaChunkEntity); } } @@ -1730,7 +1747,7 @@ internal void RemoveSharedComponentReference_Unmanaged(int sharedComponentIndex, int itemIndex; NativeParallelMultiHashMapIterator iter; - + if (m_HashLookup.TryGetFirstValue(GetSharedComponentHashKey(componentTypeIndex, hashCode), out itemIndex, out iter)) { do @@ -1891,7 +1908,7 @@ public bool AllSharedComponentReferencesAreFromChunks(UnsafeParallelHashMapArchetype->NumSharedComponents > kMaxSharedComponentCount) + var entityBatchArchetype = ecs->GetArchetype(entityBatch.Chunk); + if (nSharedComponentsToAdd + entityBatchArchetype->NumSharedComponents > kMaxSharedComponentCount) { *foundError = 1; return; @@ -1960,11 +1978,11 @@ private static void EntityBatchFromEntityChunkDataShared(in EntityInChunk* chunk entityIndex++; } - if (entityBatch.Chunk == null) + if (entityBatch.Chunk == ChunkIndex.Null) return; // Deleted Entity (not an error) - if (nSharedComponentsToAdd + entityBatch.Chunk->Archetype->NumSharedComponents > kMaxSharedComponentCount) + if (nSharedComponentsToAdd + ecs->GetArchetype(entityBatch.Chunk)->NumSharedComponents > kMaxSharedComponentCount) { *foundError = 1; return; @@ -2024,9 +2042,12 @@ internal bool CreateEntityBatchList(NativeArray entities, int nSharedCom int foundError = 0; int finalBatchSize = 0; - EntityBatchFromEntityChunkDataShared((EntityInChunk*)entityChunkData.GetUnsafePtr(), - entityChunkData.Length, (EntityBatchInChunk*)entityBatchList.GetUnsafePtr(),&finalBatchSize, - nSharedComponentsToAdd,&foundError); + fixed (EntityComponentStore* ecs = &this) + { + EntityBatchFromEntityChunkDataShared(ecs, (EntityInChunk*)entityChunkData.GetUnsafePtr(), + entityChunkData.Length, (EntityBatchInChunk*)entityBatchList.GetUnsafePtr(),&finalBatchSize, + nSharedComponentsToAdd,&foundError); + } entityBatchList.Length = finalBatchSize; @@ -2143,6 +2164,7 @@ internal struct ChunkStore : IDisposable static readonly int chunksPerMegachunk = 1 << log2ChunksPerMegachunk; static readonly int log2MegachunksInUniverse = 14; static readonly int megachunksInUniverse = 1 << log2MegachunksInUniverse; + public static readonly int kMaximumChunkCount = 1 << (log2ChunksPerMegachunk + log2MegachunksInUniverse); public static readonly int kErrorNone = 0; public static readonly int kErrorAllocationFailed = -1; @@ -2155,7 +2177,12 @@ internal struct ChunkStore : IDisposable static readonly int Log2ChunkSizeInBytesRoundedUpToPow2 = math.tzcnt(ChunkSizeInBytesRoundedUpToPow2); static readonly int MegachunkSizeInBytes = 1 << (Log2ChunkSizeInBytesRoundedUpToPow2 + 6); -// int m_refCount; + public static void Initialize() + { + // Mark first mega chunk as used so chunk index 0 can be used as a Null value + s_chunkStore.Data.m_chunkInUse.ElementAt(0) = ~0L; + s_chunkStore.Data.m_megachunkIsFull.ElementAt(0) = 1L; + } int AllocationFailed(int offset, int count) { @@ -2165,54 +2192,16 @@ int AllocationFailed(int offset, int count) return kErrorAllocationFailed; } - static int AtomicRead(ref int value) - { - return Interlocked.Add(ref value, 0); - } - - static long AtomicRead(ref long value) + internal Chunk* GetChunkPointer(int chunkIndex) { - return Interlocked.Add(ref value, 0L); - } - - static bool IsZero(ref long value) - { - return Interlocked.Add(ref value, 0L) == 0L; - } - - Chunk* GetChunkPointer(int bitOffset) - { - var megachunkIndex = bitOffset >> log2ChunksPerMegachunk; - var chunkInMegachunk = bitOffset & (chunksPerMegachunk-1); + var megachunkIndex = chunkIndex >> log2ChunksPerMegachunk; + var chunkInMegachunk = chunkIndex & (chunksPerMegachunk-1); var megachunk = (byte*)m_megachunk.ElementAt(megachunkIndex); var chunk = megachunk + (chunkInMegachunk << Log2ChunkSizeInBytesRoundedUpToPow2); return (Chunk*)chunk; } - int Allocate(out Chunk* value, int bitOffset, int actualCount) - { - value = null; - var megachunkIndex = bitOffset >> log2ChunksPerMegachunk; - var chunkInMegachunk = bitOffset & (chunksPerMegachunk-1); - if(0 == Interlocked.Add(ref m_megachunk.ElementAt(megachunkIndex), 0)) - { - long pointer = (long) Memory.Unmanaged.Allocate(MegachunkSizeInBytes, CollectionHelper.CacheLineSize, Allocator.Persistent); - if (pointer == 0) // if the allocation failed... - return AllocationFailed(bitOffset, actualCount); - if(0 != Interlocked.CompareExchange(ref m_megachunk.ElementAt(megachunkIndex), pointer, 0)) // store the new pointer - Memory.Unmanaged.Free((void*)pointer, Allocator.Persistent); - } - value = GetChunkPointer(bitOffset); - for(var chunkInAllocation = 0; chunkInAllocation < actualCount; ++chunkInAllocation) - { - var bitOffsetOfChunkInAllocation = bitOffset + chunkInAllocation; - Chunk* chunk = GetChunkPointer(bitOffsetOfChunkInAllocation); - chunk->ChunkstoreIndex = megachunkIndex; - } - return kErrorNone; - } - - public int AllocateContiguousChunks(out Chunk* value, int requestedCount, out int actualCount) + public int AllocateContiguousChunks(out ChunkIndex value, int requestedCount, out int actualCount) { int gigachunkIndex = 0; for(; gigachunkIndex < m_megachunkIsFull.Length; ++gigachunkIndex) @@ -2220,7 +2209,7 @@ public int AllocateContiguousChunks(out Chunk* value, int requestedCount, out in break; int firstMegachunk = gigachunkIndex << 6; actualCount = math.min(chunksPerMegachunk, requestedCount); // literally can't service requests for more - value = null; + value = ChunkIndex.Null; while(actualCount > 0) { for(int offset = 0; offset < megachunksInUniverse; ++offset) @@ -2239,24 +2228,18 @@ public int AllocateContiguousChunks(out Chunk* value, int requestedCount, out in newMask = ~0L; // mark the whole megachunk as full (busy) until we're done allocating memory readMask = Interlocked.CompareExchange(ref m_chunkInUse.ElementAt(megachunkIndex), newMask, oldMask); } while(readMask != oldMask); - int chunkIndex = (megachunkIndex << log2ChunksPerMegachunk) + chunkInMegachunk; + int firstChunkIndex = (megachunkIndex << log2ChunksPerMegachunk) + chunkInMegachunk; if(oldMask == 0L) // if we are the first allocation in this chunk... { long allocated = (long)Memory.Unmanaged.Allocate(MegachunkSizeInBytes, CollectionHelper.CacheLineSize, Allocator.Persistent); // allocate memory if (allocated == 0L) // if the allocation failed... - return AllocationFailed(chunkIndex, actualCount); + return AllocationFailed(firstChunkIndex, actualCount); Interlocked.Exchange(ref m_megachunk.ElementAt(megachunkIndex), allocated); // store the pointer to the freshly allocated memory Interlocked.Exchange(ref m_chunkInUse.ElementAt(megachunkIndex), maskAfterAllocation); // change the mask from ~0L to the true mask after our allocation (which may be ~0L) } if(maskAfterAllocation == ~0L) ConcurrentMask.AtomicOr(ref m_megachunkIsFull.ElementAt(megachunkIndex>>6), 1L << (megachunkIndex & 63)); - value = GetChunkPointer(chunkIndex); - for(var chunkInAllocation = 0; chunkInAllocation < actualCount; ++chunkInAllocation) - { - var bitOffsetOfChunkInAllocation = chunkIndex + chunkInAllocation; - Chunk* chunk = GetChunkPointer(bitOffsetOfChunkInAllocation); - chunk->ChunkstoreIndex = megachunkIndex; - } + value = new ChunkIndex(firstChunkIndex); return kErrorNone; NEXT_MEGACHUNK:; } @@ -2266,52 +2249,24 @@ public int AllocateContiguousChunks(out Chunk* value, int requestedCount, out in } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] - private static void ThrowMegachunkIndexIsInvalid(int megachunkIndex) + void ThrowChunkAlreadyMarkedAsFree(ChunkIndex chunk) { - throw new ArgumentException($"Megachunk index {megachunkIndex} is not beween 0 and {megachunksInUniverse-1}, inclusive"); + throw new ArgumentException($"Chunk index {(int)chunk} (address {(long)chunk.GetPtr():x8}) already marked as free"); } - [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] - private void ThrowChunkPointerIsNotInMegachunk(Chunk* chunk, int megachunkIndex) + public int FreeContiguousChunks(ChunkIndex firstChunk, int count) { - throw new ArgumentException($"Chunk pointer {(IntPtr)chunk} is not in megachunk {megachunkIndex}"); - } + var megachunkIndex = firstChunk >> log2ChunksPerMegachunk; + var chunkIndexInMegachunk = firstChunk & (chunksPerMegachunk - 1); - [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] - private void ThrowChunkAlreadyMarkedAsFree(Chunk* chunk) - { - throw new ArgumentException($"Chunk pointer {(IntPtr)chunk} already marked as free"); - } - - public int FreeContiguousChunks(Chunk* value, int count) - { - var megachunkIndex = value->ChunkstoreIndex; - megachunkIndex &= megachunksInUniverse - 1; - byte* begin = (byte*)m_megachunk.ElementAt(megachunkIndex); - byte* end = begin + MegachunkSizeInBytes; - if (!(value >= begin && value < end)) - { - for(megachunkIndex = 0; megachunkIndex < megachunksInUniverse; ++megachunkIndex) - { - begin = (byte*)m_megachunk.ElementAt(megachunkIndex); - end = begin + MegachunkSizeInBytes; - if(value >= begin && value < end) - break; - } - if(megachunkIndex == megachunksInUniverse) - { - return kErrorChunkNotFound; - } - } - int chunkInMegachunk = (int)((byte*)value - begin) >> Log2ChunkSizeInBytesRoundedUpToPow2; - long chunksToFree = ConcurrentMask.MakeMask(chunkInMegachunk, count); + long chunksToFree = ConcurrentMask.MakeMask(chunkIndexInMegachunk, count); long oldMask, newMask, readMask = m_chunkInUse.ElementAt(megachunkIndex); // read the mask of which chunks are allocated do { oldMask = readMask; if((oldMask & chunksToFree) != chunksToFree) // if any of our chunks were already freed, { - ThrowChunkAlreadyMarkedAsFree(value); // pretty serious error! throw, + ThrowChunkAlreadyMarkedAsFree(firstChunk); // pretty serious error! throw, return kErrorChunkAlreadyMarkedFree; // and return an error code. } newMask = oldMask & ~chunksToFree; // zero out the chunks to free in the mask @@ -2321,9 +2276,9 @@ public int FreeContiguousChunks(Chunk* value, int count) } while (readMask != oldMask); if(newMask == ~0L) // we set the whole mask, we aren't done until we free the memory and then zero the whole mask. { - Interlocked.Exchange(ref m_megachunk.ElementAt(megachunkIndex), 0L); // set the pointer to 0. + var oldMegachunk = Interlocked.Exchange(ref m_megachunk.ElementAt(megachunkIndex), 0L); // set the pointer to 0. Interlocked.Exchange(ref m_chunkInUse.ElementAt(megachunkIndex), 0L); // set the word to 0. "come allocate from me!" - Memory.Unmanaged.Free(begin, Allocator.Persistent); // free the megachunk, since nobody can see it anymore. + Memory.Unmanaged.Free((byte*)oldMegachunk, Allocator.Persistent); // free the megachunk, since nobody can see it anymore. } ConcurrentMask.AtomicAnd(ref m_megachunkIsFull.ElementAt(megachunkIndex>>6), ~(1L << (megachunkIndex & 63))); return kErrorNone; @@ -2343,43 +2298,33 @@ public void Dispose() internal static readonly SharedStatic s_chunkStore = SharedStatic.GetOrCreate(); - public static int AllocateContiguousChunk(int requestedCount, out Chunk* chunk, out int actualCount) + public static int AllocateContiguousChunk(int requestedCount, out ChunkIndex chunk, out int actualCount) { return s_chunkStore.Data.AllocateContiguousChunks(out chunk, requestedCount, out actualCount); } - public static int FreeContiguousChunks(Chunk* chunks, int count) - { - return s_chunkStore.Data.FreeContiguousChunks(chunks, count); - } - - public static void GetChunkMemoryStats(out long reservedPages, out long committedPages, out ulong reservedBytes, out ulong committedBytes, out long pageSizeInBytes) + public static int FreeContiguousChunks(ChunkIndex firstChunk, int count) { - reservedPages = 0; - reservedBytes = 0; - committedPages = 0; - committedBytes = 0; - pageSizeInBytes = 0; + return s_chunkStore.Data.FreeContiguousChunks(firstChunk, count); } - public Chunk* AllocateChunk() + public ChunkIndex AllocateChunk() { - Chunk* newChunk; - // Allocate new chunk - var success = s_chunkStore.Data.AllocateContiguousChunks(out newChunk, 1, out _); - Assert.IsTrue(success == 0); - Assert.IsTrue(newChunk != null); + if(s_chunkStore.Data.AllocateContiguousChunks(out var newChunk, 1, out _) != ChunkStore.kErrorNone) + { + throw new InvalidOperationException($"ChunkStore.AllocateContiguousChunks failed"); + } if (useMemoryInitPattern != 0) { - var temp = newChunk->ChunkstoreIndex; - UnsafeUtility.MemSet(newChunk, memoryInitPattern, Chunk.kChunkSize); - newChunk->ChunkstoreIndex = temp; + var raw = newChunk.GetPtr(); + UnsafeUtility.MemSet(raw, memoryInitPattern, Chunk.kChunkSize); } + return newChunk; } - public void FreeChunk(Chunk* chunk) + public void FreeChunk(ChunkIndex chunk) { var success = s_chunkStore.Data.FreeContiguousChunks(chunk, 1); Assert.IsTrue(success == 0); @@ -2493,7 +2438,7 @@ private static void ThrowIfComponentDataSizeIsLargerThanShortMaxValue(int sizeIn dstArchetype->EnableableTypesCount = numEnableableComponents; Memory.Array.Copy(dstArchetype->EnableableTypeIndexInArchetype, (int*)enableableComponentIndexInArchetype, numEnableableComponents); dstArchetype->EntityCount = 0; - dstArchetype->ChunksWithEmptySlots = new UnsafePtrList(0, Allocator.Persistent); + dstArchetype->ChunksWithEmptySlots = new UnsafeList(0, Allocator.Persistent); dstArchetype->MatchingQueryData = new UnsafeList(0, Allocator.Persistent); dstArchetype->NextChangedArchetype = null; dstArchetype->InstantiateArchetype = null; @@ -2550,7 +2495,7 @@ private static void ThrowIfComponentDataSizeIsLargerThanShortMaxValue(int sizeIn dstArchetype->Flags |= ArchetypeFlags.HasBufferComponents; - var chunkDataSize = Chunk.GetChunkBufferSize(); + var chunkDataSize = Chunk.kChunkBufferSize; dstArchetype->ScalarEntityPatchCount = scalarEntityPatchCount; dstArchetype->BufferEntityPatchCount = bufferEntityPatchCount; @@ -2672,7 +2617,7 @@ bool MemoryOrderCompare(int lhs, int rhs) m_Archetypes.Add(dstArchetype); dstArchetype->FreeChunksBySharedComponents = new ChunkListMap(); - dstArchetype->FreeChunksBySharedComponents.Init(16); + dstArchetype->FreeChunksBySharedComponents.Init(dstArchetype, 16); m_TypeLookup.Add(dstArchetype); @@ -2803,9 +2748,9 @@ public void AssertNoQueuedManagedDeferredCommands() Assert.IsTrue(isEmpty); } - public void DeallocateManagedComponents(Chunk* chunk, int indexInChunk, int batchCount) + public void DeallocateManagedComponents(ChunkIndex chunk, int indexInChunk, int batchCount) { - var archetype = chunk->Archetype; + var archetype = GetArchetype(chunk); if (archetype->NumManagedComponents == 0) return; @@ -2815,7 +2760,7 @@ public void DeallocateManagedComponents(Chunk* chunk, int indexInChunk, int batc for (int i = 0; i < numManagedComponents; ++i) { int type = i + firstManagedComponent; - var a = (int*)ChunkDataUtility.GetComponentDataRO(chunk, 0, type); + var a = (int*)ChunkDataUtility.GetComponentDataRO(chunk, archetype, 0, type); for (int ei = 0; ei < batchCount; ++ei) { var managedComponentIndex = a[ei + indexInChunk]; diff --git a/Unity.Entities/EntityComponentStoreChangeArchetype.cs b/Unity.Entities/EntityComponentStoreChangeArchetype.cs index de74352..8c4120c 100644 --- a/Unity.Entities/EntityComponentStoreChangeArchetype.cs +++ b/Unity.Entities/EntityComponentStoreChangeArchetype.cs @@ -17,7 +17,7 @@ internal unsafe partial struct EntityComponentStore public bool AddComponent(Entity entity, ComponentType type) { var dstChunk = GetChunkWithEmptySlotsWithAddedComponent(entity, type); - if (dstChunk == null) + if (dstChunk == ChunkIndex.Null) return false; Move(entity, dstChunk); @@ -27,8 +27,9 @@ public bool AddComponent(Entity entity, ComponentType type) public void AddComponent(Entity entity, in ComponentTypeSet componentTypeSet) { var chunk = GetChunk(entity); - var newArchetype = GetArchetypeWithAddedComponents(chunk->Archetype, componentTypeSet); - if (newArchetype == chunk->Archetype) // none were removed + var archetype = GetArchetype(chunk); + var newArchetype = GetArchetypeWithAddedComponents(archetype, componentTypeSet); + if (newArchetype == archetype) // none were added return; var archetypeChunkFilter = GetArchetypeChunkFilterWithAddedComponents(chunk, newArchetype); Move(entity, ref archetypeChunkFilter); @@ -37,7 +38,7 @@ public void AddComponent(Entity entity, in ComponentTypeSet componentTypeSet) public bool RemoveComponent(Entity entity, ComponentType type) { var dstChunk = GetChunkWithEmptySlotsWithRemovedComponent(entity, type); - if (dstChunk == null) + if (dstChunk == ChunkIndex.Null) return false; Move(entity, dstChunk); @@ -49,8 +50,9 @@ public void RemoveComponent(Entity entity, in ComponentTypeSet componentTypeSet) if (Hint.Unlikely(!Exists(entity))) return; var chunk = GetChunk(entity); - var newArchetype = GetArchetypeWithRemovedComponents(chunk->Archetype, componentTypeSet); - if (newArchetype == chunk->Archetype) // none were removed + var archetype = GetArchetype(chunk); + var newArchetype = GetArchetypeWithRemovedComponents(archetype, componentTypeSet); + if (newArchetype == archetype) // none were removed return; var archetypeChunkFilter = GetArchetypeChunkFilterWithRemovedComponents(chunk, newArchetype); Move(entity, ref archetypeChunkFilter); @@ -70,9 +72,9 @@ bool AddComponent(EntityBatchInChunk entityBatchInChunk, ComponentType component bool AddComponents(EntityBatchInChunk entityBatchInChunk, in ComponentTypeSet componentTypeSet) { var srcChunk = entityBatchInChunk.Chunk; - - var dstArchetype = GetArchetypeWithAddedComponents(srcChunk->Archetype, componentTypeSet); - if (dstArchetype == srcChunk->Archetype) // none were added + var srcArchetype = GetArchetype(srcChunk); + var dstArchetype = GetArchetypeWithAddedComponents(srcArchetype, componentTypeSet); + if (dstArchetype == srcArchetype) // none were added return false; var archetypeChunkFilter = GetArchetypeChunkFilterWithAddedComponents(srcChunk, dstArchetype); @@ -97,9 +99,10 @@ bool RemoveComponent(EntityBatchInChunk entityBatchInChunk, ComponentType compon bool RemoveComponents(EntityBatchInChunk entityBatchInChunk, in ComponentTypeSet componentTypeSet) { var srcChunk = entityBatchInChunk.Chunk; + var srcArchetype = GetArchetype(srcChunk); - var dstArchetype = GetArchetypeWithRemovedComponents(srcChunk->Archetype, componentTypeSet); - if (dstArchetype == srcChunk->Archetype) // none were removed + var dstArchetype = GetArchetypeWithRemovedComponents(srcArchetype, componentTypeSet); + if (dstArchetype == srcArchetype) // none were removed return false; var archetypeChunkFilter = GetArchetypeChunkFilterWithRemovedComponents(srcChunk, dstArchetype); @@ -119,17 +122,17 @@ public void AddComponent(ArchetypeChunk* chunks, int chunkCount, ComponentType c for (int i = 0; i < chunkCount; i++) { var chunk = chunks[i].m_Chunk; - var srcArchetype = chunk->Archetype; - if (prevArchetype != chunk->Archetype) + var srcArchetype = chunks[i].Archetype.Archetype; + if (prevArchetype != srcArchetype) { dstArchetype = GetArchetypeWithAddedComponent(srcArchetype, componentType, &indexInTypeArray); - prevArchetype = chunk->Archetype; + prevArchetype = srcArchetype; } if (dstArchetype == null) continue; - var archetypeChunkFilter = GetArchetypeChunkFilterWithAddedComponent(chunk, dstArchetype, indexInTypeArray, componentType, sharedComponentIndex); + var archetypeChunkFilter = GetArchetypeChunkFilterWithAddedComponent(srcArchetype, chunk.ListIndex, dstArchetype, indexInTypeArray, componentType, sharedComponentIndex); Move(chunk, ref archetypeChunkFilter); } @@ -138,17 +141,10 @@ public void AddComponent(ArchetypeChunk* chunks, int chunkCount, ComponentType c struct ChunkAndEnabledMask : IComparable { public v128 EnabledMask; - public Chunk* Chunk; + public ChunkIndex Chunk; public int ChunkEntityCount; public byte UseEnabledMask; - public int CompareTo(ChunkAndEnabledMask other) - { - if ((ulong)Chunk < (ulong)other.Chunk) - return -1; - if ((ulong)Chunk > (ulong)other.Chunk) - return 1; - return 0; - } + public int CompareTo(ChunkAndEnabledMask other) => Chunk.CompareTo(other.Chunk); } public void AddComponent(EntityQueryImpl *queryImpl, ComponentType componentType, int sharedComponentIndex = 0) @@ -181,7 +177,7 @@ public void AddComponent(EntityQueryImpl *queryImpl, ComponentType componentType for(int iChunk=0,chunkCount=chunksToProcess.Length; iChunkArchetype; + var chunkArchetype = GetArchetype(chunk); if (Hint.Unlikely(srcArchetype != chunkArchetype)) { srcArchetype = chunkArchetype; @@ -198,7 +194,7 @@ public void AddComponent(EntityQueryImpl *queryImpl, ComponentType componentType } continue; } - var archetypeChunkFilter = GetArchetypeChunkFilterWithAddedComponent(chunk, dstArchetype, + var archetypeChunkFilter = GetArchetypeChunkFilterWithAddedComponent(srcArchetype, chunk.ListIndex, dstArchetype, indexInTypeArray, componentType, sharedComponentIndex); if (chunksToProcess[iChunk].UseEnabledMask == 0) { @@ -233,7 +229,7 @@ public void AddComponent(EntityQueryImpl *queryImpl, ComponentType componentType } // Shared codepath for SetSharedComponent(query) and AddComponent(query) - private void SetSharedComponentDataIndexForChunk(Chunk* chunk, Archetype* chunkArchetype, + void SetSharedComponentDataIndexForChunk(ChunkIndex chunk, Archetype* chunkArchetype, ComponentType componentType, int sharedComponentIndex, byte useEnabledMask, v128 chunkEnabledMask, EntityBatchInChunk* chunkBatches) { var archetypeChunkFilter = @@ -302,7 +298,7 @@ public void AddComponents(EntityQueryImpl* queryImpl, in ComponentTypeSet compon for(int iChunk=0,chunkCount=chunksToProcess.Length; iChunkArchetype; + var chunkArchetype = GetArchetype(chunk); if (Hint.Unlikely(srcArchetype != chunkArchetype)) { srcArchetype = chunkArchetype; @@ -350,12 +346,12 @@ public void RemoveComponent(ArchetypeChunk* chunks, int chunkCount, ComponentTyp for (int i = 0; i < chunkCount; i++) { var chunk = chunks[i].m_Chunk; - var srcArchetype = chunk->Archetype; + var srcArchetype = chunks[i].Archetype.Archetype; - if (prevArchetype != chunk->Archetype) + if (prevArchetype != srcArchetype) { dstArchetype = GetArchetypeWithRemovedComponent(srcArchetype, componentType, &indexInTypeArray); - prevArchetype = chunk->Archetype; + prevArchetype = srcArchetype; } if (dstArchetype == srcArchetype) @@ -397,7 +393,7 @@ public void RemoveComponent(EntityQueryImpl* queryImpl, ComponentType componentT for(int iChunk=0,chunkCount=chunksToProcess.Length; iChunkArchetype; + var chunkArchetype = GetArchetype(chunk); if (Hint.Unlikely(srcArchetype != chunkArchetype)) { srcArchetype = chunkArchetype; @@ -466,7 +462,7 @@ public void RemoveComponents(EntityQueryImpl* queryImpl, in ComponentTypeSet com for(int iChunk=0,chunkCount=chunksToProcess.Length; iChunkArchetype; + var chunkArchetype = GetArchetype(chunk); if (Hint.Unlikely(srcArchetype != chunkArchetype)) { srcArchetype = chunkArchetype; @@ -577,7 +573,7 @@ public void SetSharedComponentDataIndex(EntityQueryImpl* queryImpl, ComponentTyp for(int iChunk=0,chunkCount=chunksToProcess.Length; iChunkArchetype; + var chunkArchetype = GetArchetype(chunk); SetSharedComponentDataIndexForChunk(chunk, chunkArchetype, componentType, dstSharedComponentDataIndex, chunksToProcess[iChunk].UseEnabledMask, chunksToProcess[iChunk].EnabledMask, chunkBatches); } @@ -600,7 +596,7 @@ public void Move(Entity entity, Archetype* archetype, SharedComponentValues shar Move(entity, ref archetypeChunkFilter); } - public void Move(Chunk* srcChunk, Archetype* archetype, SharedComponentValues sharedComponentValues) + public void Move(ChunkIndex srcChunk, Archetype* archetype, SharedComponentValues sharedComponentValues) { var archetypeChunkFilter = new ArchetypeChunkFilter(archetype, sharedComponentValues); Move(srcChunk, ref archetypeChunkFilter); @@ -616,7 +612,7 @@ public void MoveAndSetChangeVersion(EntityBatchInChunk batch, Archetype* archety // INTERNAL // ---------------------------------------------------------------------------------------------------------- - void Move(Entity entity, Chunk* dstChunk) + void Move(Entity entity, ChunkIndex dstChunk) { var srcEntityInChunk = GetEntityInChunk(entity); var srcChunk = srcEntityInChunk.Chunk; @@ -635,25 +631,25 @@ void Move(Entity entity, ref ArchetypeChunkFilter archetypeChunkFilter) Move(entityBatch, ref archetypeChunkFilter); } - void Move(Chunk* srcChunk, ref ArchetypeChunkFilter archetypeChunkFilter) + void Move(ChunkIndex srcChunk, ref ArchetypeChunkFilter archetypeChunkFilter) { + var srcArchetype = GetArchetype(srcChunk); if (archetypeChunkFilter.Archetype->CleanupComplete) { - ChunkDataUtility.Deallocate(srcChunk); + ChunkDataUtility.Deallocate(srcArchetype, srcChunk); return; } - var srcArchetype = srcChunk->Archetype; if (ChunkDataUtility.AreLayoutCompatible(srcArchetype, archetypeChunkFilter.Archetype)) { fixed(int* sharedComponentValues = archetypeChunkFilter.SharedComponentValues) { - ChunkDataUtility.ChangeArchetypeInPlace(srcChunk, archetypeChunkFilter.Archetype, sharedComponentValues); + ChunkDataUtility.ChangeArchetypeInPlace(srcArchetype, srcChunk, archetypeChunkFilter.Archetype, sharedComponentValues); } return; } - var entityBatch = new EntityBatchInChunk { Chunk = srcChunk, Count = srcChunk->Count, StartIndex = 0 }; + var entityBatch = new EntityBatchInChunk { Chunk = srcChunk, Count = srcChunk.Count, StartIndex = 0 }; Move(entityBatch, ref archetypeChunkFilter); } @@ -665,9 +661,10 @@ void Move(EntityBatchInChunk entityBatchInChunk, ref ArchetypeChunkFilter archet var srcRemainingCount = entityBatchInChunk.Count; var startIndex = entityBatchInChunk.StartIndex; - if ((srcRemainingCount == srcChunk->Count) && cleanupComplete) + if ((srcRemainingCount == srcChunk.Count) && cleanupComplete) { - ChunkDataUtility.Deallocate(srcChunk); + var srcArchetype = GetArchetype(srcChunk); + ChunkDataUtility.Deallocate(srcArchetype, srcChunk); return; } @@ -689,9 +686,10 @@ void MoveAndSetChangeVersion(EntityBatchInChunk entityBatchInChunk, ref Archetyp var srcRemainingCount = entityBatchInChunk.Count; var startIndex = entityBatchInChunk.StartIndex; - if ((srcRemainingCount == srcChunk->Count) && cleanupComplete) + if ((srcRemainingCount == srcChunk.Count) && cleanupComplete) { - ChunkDataUtility.Deallocate(srcChunk); + var srcArchetype = GetArchetype(srcChunk); + ChunkDataUtility.Deallocate(srcArchetype, srcChunk); return; } @@ -703,7 +701,7 @@ void MoveAndSetChangeVersion(EntityBatchInChunk entityBatchInChunk, ref Archetyp var dstChunk = GetChunkWithEmptySlots(ref archetypeChunkFilter); var dstCount = Move(new EntityBatchInChunk { Chunk = srcChunk, Count = srcRemainingCount, StartIndex = startIndex }, dstChunk); srcRemainingCount -= dstCount; - dstArchetype->Chunks.SetChangeVersion(typeIndexInDstArchetype, dstChunk->ListIndex, + dstArchetype->Chunks.SetChangeVersion(typeIndexInDstArchetype, dstChunk.ListIndex, GlobalSystemVersion); } } @@ -720,10 +718,13 @@ void MoveAndSetChangeVersion(EntityBatchInChunk entityBatchInChunk, ref Archetyp /// Chunks can be of same archetype (but differ by shared component values). /// /// Returns the number moved. Caller handles if less than indicated in srcBatch. - int Move(in EntityBatchInChunk srcBatch, Chunk* dstChunk) + int Move(in EntityBatchInChunk srcBatch, ChunkIndex dstChunk) { var srcChunk = srcBatch.Chunk; - var srcCount = math.min(dstChunk->UnusedCount, srcBatch.Count); + var srcArchetype = GetArchetype(srcChunk); + var dstArchetype = GetArchetype(dstChunk); + var dstUnusedCount = dstArchetype->ChunkCapacity - dstChunk.Count; + var srcCount = math.min(dstUnusedCount, srcBatch.Count); var srcStartIndex = srcBatch.StartIndex + srcBatch.Count - srcCount; var partialSrcBatch = new EntityBatchInChunk @@ -733,8 +734,12 @@ int Move(in EntityBatchInChunk srcBatch, Chunk* dstChunk) Count = srcCount }; - ChunkDataUtility.Clone(partialSrcBatch, dstChunk); - ChunkDataUtility.Remove(partialSrcBatch); + ChunkDataUtility.Clone(srcArchetype, partialSrcBatch, dstArchetype, dstChunk); + + fixed (EntityComponentStore* store = &this) + { + ChunkDataUtility.Remove(store, partialSrcBatch); + } return srcCount; } diff --git a/Unity.Entities/EntityComponentStoreChunk.cs b/Unity.Entities/EntityComponentStoreChunk.cs index 2d24f63..932da53 100644 --- a/Unity.Entities/EntityComponentStoreChunk.cs +++ b/Unity.Entities/EntityComponentStoreChunk.cs @@ -21,7 +21,7 @@ internal unsafe partial struct EntityComponentStore internal bool IsComponentEnabled(Entity entity, TypeIndex typeIndex) { var chunk = m_EntityInChunkByEntity[entity.Index].Chunk; - var archetype = chunk->Archetype; + var archetype = GetArchetype(chunk); var indexInChunk = m_EntityInChunkByEntity[entity.Index].IndexInChunk; var typeOffset = ChunkDataUtility.GetIndexInTypeArray(archetype, typeIndex); @@ -31,7 +31,7 @@ internal bool IsComponentEnabled(Entity entity, TypeIndex typeIndex) internal bool IsComponentEnabled(Entity entity, TypeIndex typeIndex, ref LookupCache typeLookupCache) { var chunk = m_EntityInChunkByEntity[entity.Index].Chunk; - var archetype = chunk->Archetype; + var archetype = GetArchetype(chunk); if (Hint.Unlikely(archetype != typeLookupCache.Archetype)) typeLookupCache.Update(archetype, typeIndex); var indexInChunk = m_EntityInChunkByEntity[entity.Index].IndexInChunk; @@ -39,22 +39,25 @@ internal bool IsComponentEnabled(Entity entity, TypeIndex typeIndex, ref LookupC return IsComponentEnabled(chunk, indexInChunk, typeLookupCache.IndexInArchetype); } - internal bool IsComponentEnabled(Chunk* chunk, int indexInChunk, int typeIndexInArchetype) + internal bool IsComponentEnabled(ChunkIndex chunk, int indexInChunk, int typeIndexInArchetype) { + var archetype = GetArchetype(chunk); + #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG // the bitmask size is always 128 bits, so make sure we're not indexing outside the chunk's capacity. - if (Hint.Unlikely(indexInChunk < 0 || indexInChunk >= chunk->Capacity)) - throw new ArgumentException($"indexInChunk {indexInChunk} is outside the valid range [0..{chunk->Capacity-1}]"); + var capacity = archetype->ChunkCapacity; + if (Hint.Unlikely(indexInChunk < 0 || indexInChunk >= capacity)) + throw new ArgumentException($"indexInChunk {indexInChunk} is outside the valid range [0..{capacity - 1}]"); #endif - var isComponentEnabled = ChunkDataUtility.GetEnabledRefRO(chunk, typeIndexInArchetype); + var isComponentEnabled = ChunkDataUtility.GetEnabledRefRO(chunk, archetype, typeIndexInArchetype); return isComponentEnabled.IsSet(indexInChunk); } internal void SetComponentEnabled(Entity entity, TypeIndex typeIndex, bool value) { var chunk = m_EntityInChunkByEntity[entity.Index].Chunk; - var archetype = chunk->Archetype; + var archetype = GetArchetype(chunk); var indexInChunk = m_EntityInChunkByEntity[entity.Index].IndexInChunk; var typeOffset = ChunkDataUtility.GetIndexInTypeArray(archetype, typeIndex); @@ -64,7 +67,7 @@ internal void SetComponentEnabled(Entity entity, TypeIndex typeIndex, bool value internal void SetComponentEnabled(Entity entity, TypeIndex typeIndex, bool value, ref LookupCache typeLookupCache) { var chunk = m_EntityInChunkByEntity[entity.Index].Chunk; - var archetype = chunk->Archetype; + var archetype = GetArchetype(chunk); var indexInChunk = m_EntityInChunkByEntity[entity.Index].IndexInChunk; if (Hint.Unlikely(archetype != typeLookupCache.Archetype)) typeLookupCache.Update(archetype, typeIndex); @@ -72,15 +75,18 @@ internal void SetComponentEnabled(Entity entity, TypeIndex typeIndex, bool value SetComponentEnabled(chunk, indexInChunk, typeLookupCache.IndexInArchetype, value); } - internal void SetComponentEnabled(Chunk* chunk, int indexInChunk, int typeIndexInArchetype, bool value) + internal void SetComponentEnabled(ChunkIndex chunk, int indexInChunk, int typeIndexInArchetype, bool value) { + var archetype = GetArchetype(chunk); + var capacity = archetype->ChunkCapacity; + #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG // the bit array size is padded up to 64 bits, so we validate we're not indexing outside the valid data. - if (Hint.Unlikely(indexInChunk < 0 || indexInChunk >= chunk->Capacity)) - throw new ArgumentException($"indexInChunk {indexInChunk} is outside the valid range [0..{chunk->Capacity-1}]"); + if (Hint.Unlikely(indexInChunk < 0 || indexInChunk >= capacity)) + throw new ArgumentException($"indexInChunk {indexInChunk} is outside the valid range [0..{capacity - 1}]"); #endif - var bits = ChunkDataUtility.GetEnabledRefRW(chunk, typeIndexInArchetype, out var ptrChunkDisabledCount); + var bits = ChunkDataUtility.GetEnabledRefRW(chunk, archetype, typeIndexInArchetype, GlobalSystemVersion, out var ptrChunkDisabledCount); var numStridesIntoBits = (indexInChunk / 64); var pBits = bits.Ptr + numStridesIntoBits; var indexInPBits = indexInChunk - (numStridesIntoBits * 64); @@ -121,11 +127,11 @@ internal void SetComponentEnabled(Chunk* chunk, int indexInChunk, int typeIndexI { AssertEntityHasComponent(entity, typeIndex, ref typeLookupCache); var chunk = m_EntityInChunkByEntity[entity.Index].Chunk; - var archetype = chunk->Archetype; + var archetype = GetArchetype(chunk); indexInBitField = m_EntityInChunkByEntity[entity.Index].IndexInChunk; if (Hint.Unlikely(archetype != typeLookupCache.Archetype)) typeLookupCache.Update(archetype, typeIndex); - return ChunkDataUtility.GetEnabledRefRW(m_EntityInChunkByEntity[entity.Index].Chunk, typeLookupCache.IndexInArchetype, globalSystemVersion, out ptrChunkDisabledCount).Ptr; + return ChunkDataUtility.GetEnabledRefRW(m_EntityInChunkByEntity[entity.Index].Chunk, archetype, typeLookupCache.IndexInArchetype, globalSystemVersion, out ptrChunkDisabledCount).Ptr; } /// @@ -142,13 +148,13 @@ internal void SetComponentEnabled(Chunk* chunk, int indexInChunk, int typeIndexI { AssertEntityHasComponent(entity, typeIndex, ref typeLookupCache); var chunk = m_EntityInChunkByEntity[entity.Index].Chunk; - var archetype = chunk->Archetype; + var archetype = GetArchetype(chunk); indexInBitField = m_EntityInChunkByEntity[entity.Index].IndexInChunk; if (Hint.Unlikely(archetype != typeLookupCache.Archetype)) typeLookupCache.Update(archetype, typeIndex); int memoryOrderIndexInArchetype = archetype->TypeIndexInArchetypeToMemoryOrderIndex[typeLookupCache.IndexInArchetype]; - ptrChunkDisabledCount = archetype->Chunks.GetPointerToChunkDisabledCountForType(memoryOrderIndexInArchetype, chunk->ListIndex); - return ChunkDataUtility.GetEnabledRefRO(chunk, typeLookupCache.IndexInArchetype).Ptr; + ptrChunkDisabledCount = archetype->Chunks.GetPointerToChunkDisabledCountForType(memoryOrderIndexInArchetype, chunk.ListIndex); + return ChunkDataUtility.GetEnabledRefRO(chunk, archetype, typeLookupCache.IndexInArchetype).Ptr; } // | ChangeVersion | OrderVersion | @@ -165,12 +171,12 @@ internal void SetComponentEnabled(Chunk* chunk, int indexInChunk, int typeIndexI // OrderVersion : e.g. Should I re-allocate a lookaside cache based on chunk data? - internal void AllocateEntities(Archetype* arch, Chunk* chunk, int baseIndex, int count, Entity* outputEntities) + internal void AllocateEntities(Archetype* arch, ChunkIndex chunk, int baseIndex, int count, Entity* outputEntities) { - Assert.AreEqual(chunk->Archetype->Offsets[0], 0); - Assert.AreEqual(chunk->Archetype->SizeOfs[0], sizeof(Entity)); + Assert.AreEqual(arch->Offsets[0], 0); + Assert.AreEqual(arch->SizeOfs[0], sizeof(Entity)); - var entityInChunkStart = (Entity*)chunk->Buffer + baseIndex; + var entityInChunkStart = (Entity*)chunk.Buffer + baseIndex; for (var i = 0; i != count; i++) { @@ -195,7 +201,6 @@ internal void AllocateEntities(Archetype* arch, Chunk* chunk, int baseIndex, int entityInChunk->Version = entityVersion; m_EntityInChunkByEntity[m_NextFreeEntityIndex].IndexInChunk = baseIndex + i; - m_ArchetypeByEntity[m_NextFreeEntityIndex] = arch; m_EntityInChunkByEntity[m_NextFreeEntityIndex].Chunk = chunk; #if !DOTS_DISABLE_DEBUG_NAMES m_NameByEntity[m_NextFreeEntityIndex] = new EntityName(); @@ -206,19 +211,19 @@ internal void AllocateEntities(Archetype* arch, Chunk* chunk, int baseIndex, int } } - internal void DeallocateDataEntitiesInChunk(Chunk* chunk, int indexInChunk, int batchCount) + internal void DeallocateDataEntitiesInChunk(ChunkIndex chunk, Archetype* archetype, int indexInChunk, int batchCount) { DeallocateBuffers(chunk, indexInChunk, batchCount); DeallocateManagedComponents(chunk, indexInChunk, batchCount); var freeIndex = m_NextFreeEntityIndex; - var entities = (Entity*)chunk->Buffer + indexInChunk; + var entities = (Entity*)chunk.Buffer + indexInChunk; for (var i = batchCount - 1; i >= 0; --i) { var entityIndex = entities[i].Index; - m_EntityInChunkByEntity[entityIndex].Chunk = null; + m_EntityInChunkByEntity[entityIndex].Chunk = ChunkIndex.Null; m_VersionByEntity[entityIndex]++; m_EntityInChunkByEntity[entityIndex].IndexInChunk = freeIndex; #if !DOTS_DISABLE_DEBUG_NAMES @@ -232,42 +237,48 @@ internal void DeallocateDataEntitiesInChunk(Chunk* chunk, int indexInChunk, int m_EntityCreateDestroyVersion++; // Compute the number of things that need to moved and patched. - int patchCount = Math.Min(batchCount, chunk->Count - indexInChunk - batchCount); + int patchCount = Math.Min(batchCount, chunk.Count - indexInChunk - batchCount); - ChunkDataUtility.RemoveFromEnabledBitsHierarchicalData(chunk, indexInChunk, batchCount); + ChunkDataUtility.RemoveFromEnabledBitsHierarchicalData(chunk, archetype, indexInChunk, batchCount); if (0 == patchCount) { // if we're not patching, we still need to clear the padding bits for the entities we destroyed - ChunkDataUtility.ClearPaddingBits(chunk, indexInChunk, batchCount); + ChunkDataUtility.ClearPaddingBits(chunk, archetype, indexInChunk, batchCount); return; } // updates indexInChunk to point to where the components will be moved to //Assert.IsTrue(chunk->archetype->sizeOfs[0] == sizeof(Entity) && chunk->archetype->offsets[0] == 0); - var movedEntities = (Entity*)chunk->Buffer + (chunk->Count - patchCount); + var movedEntities = (Entity*)chunk.Buffer + (chunk.Count - patchCount); for (var i = 0; i != patchCount; i++) m_EntityInChunkByEntity[movedEntities[i].Index].IndexInChunk = indexInChunk + i; // Move component data from the end to where we deleted components - var startIndex = chunk->Count - patchCount; - ChunkDataUtility.Copy(chunk, startIndex, chunk, indexInChunk, patchCount); - ChunkDataUtility.CloneEnabledBits(chunk, startIndex, chunk, indexInChunk, patchCount); - var clearStartIndex = chunk->Count - batchCount; - ChunkDataUtility.ClearPaddingBits(chunk, clearStartIndex, batchCount); + var startIndex = chunk.Count - patchCount; + + fixed (EntityComponentStore* store = &this) + { + ChunkDataUtility.Copy(store, chunk, startIndex, chunk, indexInChunk, patchCount); + } + + ChunkDataUtility.CloneEnabledBits(chunk, archetype, startIndex, chunk, archetype, indexInChunk, patchCount); + var clearStartIndex = chunk.Count - batchCount; + ChunkDataUtility.ClearPaddingBits(chunk, archetype, clearStartIndex, batchCount); } - void DeallocateBuffers(Chunk* chunk, int indexInChunk, int batchCount) + void DeallocateBuffers(ChunkIndex chunk, int indexInChunk, int batchCount) { - var archetype = chunk->Archetype; + var archetype = GetArchetype(chunk); + var chunkBuffer = chunk.Buffer; - for (var ti = 0; ti < archetype->TypesCount; ++ti) + for (int ti = 0, count = archetype->TypesCount; ti < count; ++ti) { var type = archetype->Types[ti]; if (!type.IsBuffer) continue; - var basePtr = chunk->Buffer + archetype->Offsets[ti]; + var basePtr = chunkBuffer + archetype->Offsets[ti]; var stride = archetype->SizeOfs[ti]; for (int i = 0; i < batchCount; ++i) diff --git a/Unity.Entities/EntityComponentStoreCreateArchetype.cs b/Unity.Entities/EntityComponentStoreCreateArchetype.cs index 7e2b1ce..346721b 100644 --- a/Unity.Entities/EntityComponentStoreCreateArchetype.cs +++ b/Unity.Entities/EntityComponentStoreCreateArchetype.cs @@ -164,69 +164,70 @@ public ArchetypeChunkFilter(Archetype* archetype, SharedComponentValues sharedCo } } - Chunk* GetChunkWithEmptySlotsWithAddedComponent(Entity entity, ComponentType componentType) + ChunkIndex GetChunkWithEmptySlotsWithAddedComponent(Entity entity, ComponentType componentType) { if (!Exists(entity)) - return null; + return ChunkIndex.Null; return GetChunkWithEmptySlotsWithAddedComponent(GetChunk(entity), componentType); } - Chunk* GetChunkWithEmptySlotsWithAddedComponent(Chunk* srcChunk, ComponentType componentType, int sharedComponentIndex = 0) + ChunkIndex GetChunkWithEmptySlotsWithAddedComponent(ChunkIndex srcChunk, ComponentType componentType, int sharedComponentIndex = 0) { var archetypeChunkFilter = GetArchetypeChunkFilterWithAddedComponent(srcChunk, componentType, sharedComponentIndex); if (archetypeChunkFilter.Archetype == null) - return null; + return ChunkIndex.Null; return GetChunkWithEmptySlots(ref archetypeChunkFilter); } - Chunk* GetChunkWithEmptySlotsWithRemovedComponent(Entity entity, ComponentType componentType) + ChunkIndex GetChunkWithEmptySlotsWithRemovedComponent(Entity entity, ComponentType componentType) { if (!Exists(entity)) - return null; + return ChunkIndex.Null; return GetChunkWithEmptySlotsWithRemovedComponent(GetChunk(entity), componentType); } - Chunk* GetChunkWithEmptySlotsWithRemovedComponent(Chunk* srcChunk, ComponentType componentType) + ChunkIndex GetChunkWithEmptySlotsWithRemovedComponent(ChunkIndex srcChunk, ComponentType componentType) { var archetypeChunkFilter = GetArchetypeChunkFilterWithRemovedComponent(srcChunk, componentType); if (archetypeChunkFilter.Archetype == null) - return null; + return ChunkIndex.Null; return GetChunkWithEmptySlots(ref archetypeChunkFilter); } - Chunk* GetChunkWithEmptySlots(ref ArchetypeChunkFilter archetypeChunkFilter) + ChunkIndex GetChunkWithEmptySlots(ref ArchetypeChunkFilter archetypeChunkFilter) { var archetype = archetypeChunkFilter.Archetype; fixed(int* sharedComponentValues = archetypeChunkFilter.SharedComponentValues) { var chunk = archetype->GetExistingChunkWithEmptySlots(sharedComponentValues); - if (chunk == null) + if (chunk == ChunkIndex.Null) chunk = GetCleanChunk(archetype, sharedComponentValues); return chunk; } } - ArchetypeChunkFilter GetArchetypeChunkFilterWithChangedArchetype(Chunk* srcChunk, Archetype* dstArchetype) + ArchetypeChunkFilter GetArchetypeChunkFilterWithChangedArchetype(ChunkIndex srcChunk, Archetype* dstArchetype) { - var srcArchetype = srcChunk->Archetype; + var srcArchetype = GetArchetype(srcChunk); var archetypeChunkFilter = new ArchetypeChunkFilter(); archetypeChunkFilter.Archetype = dstArchetype; - BuildSharedComponentIndicesWithChangedArchetype(srcArchetype, dstArchetype, srcChunk->SharedComponentValues, archetypeChunkFilter.SharedComponentValues); + var srcSharedComponentValues = srcArchetype->Chunks.GetSharedComponentValues(srcChunk.ListIndex); + BuildSharedComponentIndicesWithChangedArchetype(srcArchetype, dstArchetype, srcSharedComponentValues, archetypeChunkFilter.SharedComponentValues); return archetypeChunkFilter; } - ArchetypeChunkFilter GetArchetypeChunkFilterWithChangedSharedComponent(Chunk* srcChunk, ComponentType componentType, int dstSharedComponentIndex) + ArchetypeChunkFilter GetArchetypeChunkFilterWithChangedSharedComponent(ChunkIndex srcChunk, ComponentType componentType, int dstSharedComponentIndex) { var typeIndex = componentType.TypeIndex; - var srcArchetype = srcChunk->Archetype; + var srcArchetype = GetArchetype(srcChunk); var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(srcArchetype, typeIndex); - var srcSharedComponentValueArray = srcChunk->SharedComponentValues; + var srcSharedComponentValueArray = srcArchetype->Chunks.GetSharedComponentValues(srcChunk.ListIndex); var sharedComponentOffset = indexInTypeArray - srcArchetype->FirstSharedComponent; var srcSharedComponentIndex = srcSharedComponentValueArray[sharedComponentOffset]; @@ -241,28 +242,28 @@ ArchetypeChunkFilter GetArchetypeChunkFilterWithChangedSharedComponent(Chunk* sr return archetypeChunkFilter; } - ArchetypeChunkFilter GetArchetypeChunkFilterWithAddedComponent(Chunk* srcChunk, Archetype* dstArchetype, int indexInTypeArray, ComponentType componentType, int sharedComponentIndex) + ArchetypeChunkFilter GetArchetypeChunkFilterWithAddedComponent(Archetype* srcArchetype, int srcChunkListIndex, Archetype* dstArchetype, int indexInTypeArray, ComponentType componentType, int sharedComponentIndex) { - var srcArchetype = srcChunk->Archetype; var archetypeChunkFilter = new ArchetypeChunkFilter(); archetypeChunkFilter.Archetype = dstArchetype; + var srcSharedComponentValues = srcArchetype->Chunks.GetSharedComponentValues(srcChunkListIndex); if (componentType.IsSharedComponent) { int indexOfNewSharedComponent = indexInTypeArray - dstArchetype->FirstSharedComponent; - BuildSharedComponentIndicesWithAddedComponent(indexOfNewSharedComponent, sharedComponentIndex, dstArchetype->NumSharedComponents, srcChunk->SharedComponentValues, archetypeChunkFilter.SharedComponentValues); + BuildSharedComponentIndicesWithAddedComponent(indexOfNewSharedComponent, sharedComponentIndex, dstArchetype->NumSharedComponents, srcSharedComponentValues, archetypeChunkFilter.SharedComponentValues); } else { for (int i = 0; i < srcArchetype->NumSharedComponents; i++) - archetypeChunkFilter.SharedComponentValues[i] = srcChunk->SharedComponentValues[i]; + archetypeChunkFilter.SharedComponentValues[i] = srcSharedComponentValues[i]; } return archetypeChunkFilter; } - ArchetypeChunkFilter GetArchetypeChunkFilterWithAddedComponent(Chunk* srcChunk, ComponentType componentType, int sharedComponentIndex) + ArchetypeChunkFilter GetArchetypeChunkFilterWithAddedComponent(ChunkIndex srcChunk, ComponentType componentType, int sharedComponentIndex) { - var srcArchetype = srcChunk->Archetype; + var srcArchetype = GetArchetype(srcChunk); int indexInTypeArray = 0; var dstArchetype = GetArchetypeWithAddedComponent(srcArchetype, componentType, &indexInTypeArray); if (dstArchetype == null) @@ -273,24 +274,24 @@ ArchetypeChunkFilter GetArchetypeChunkFilterWithAddedComponent(Chunk* srcChunk, Assert.IsTrue(dstArchetype->NumSharedComponents <= kMaxSharedComponentCount); - return GetArchetypeChunkFilterWithAddedComponent(srcChunk, dstArchetype, indexInTypeArray, componentType, sharedComponentIndex); + return GetArchetypeChunkFilterWithAddedComponent(srcArchetype, srcChunk.ListIndex, dstArchetype, indexInTypeArray, componentType, sharedComponentIndex); } - - ArchetypeChunkFilter GetArchetypeChunkFilterWithAddedComponents(Chunk* srcChunk, Archetype* dstArchetype) + ArchetypeChunkFilter GetArchetypeChunkFilterWithAddedComponents(ChunkIndex srcChunk, Archetype* dstArchetype) { - var srcArchetype = srcChunk->Archetype; + var srcArchetype = GetArchetype(srcChunk); var archetypeChunkFilter = new ArchetypeChunkFilter(); archetypeChunkFilter.Archetype = dstArchetype; var numSrcSharedComponents = srcArchetype->NumSharedComponents; + var srcSharedComponentValues = srcArchetype->Chunks.GetSharedComponentValues(srcChunk.ListIndex); if (dstArchetype->NumSharedComponents > numSrcSharedComponents) { - BuildSharedComponentIndicesWithAddedComponents(srcArchetype, dstArchetype, srcChunk->SharedComponentValues, archetypeChunkFilter.SharedComponentValues); + BuildSharedComponentIndicesWithAddedComponents(srcArchetype, dstArchetype, srcSharedComponentValues, archetypeChunkFilter.SharedComponentValues); } else { for (int i = 0; i < numSrcSharedComponents; i++) - archetypeChunkFilter.SharedComponentValues[i] = srcChunk->SharedComponentValues[i]; + archetypeChunkFilter.SharedComponentValues[i] = srcSharedComponentValues[i]; } return archetypeChunkFilter; } @@ -343,28 +344,29 @@ ArchetypeChunkFilter GetArchetypeChunkFilterWithAddedComponents(Chunk* srcChunk, return GetOrCreateArchetype(dstTypes + unusedIndices, dstTypesCount - unusedIndices); } - ArchetypeChunkFilter GetArchetypeChunkFilterWithRemovedComponent(Chunk* srcChunk, Archetype* dstArchetype, int indexInTypeArray, ComponentType componentType) + ArchetypeChunkFilter GetArchetypeChunkFilterWithRemovedComponent(ChunkIndex srcChunk, Archetype* dstArchetype, int indexInTypeArray, ComponentType componentType) { - var srcArchetype = srcChunk->Archetype; + var srcArchetype = GetArchetype(srcChunk); var archetypeChunkFilter = new ArchetypeChunkFilter(); archetypeChunkFilter.Archetype = dstArchetype; + var srcSharedComponentValues = srcArchetype->Chunks.GetSharedComponentValues(srcChunk.ListIndex); if (componentType.IsSharedComponent) { int indexOfRemovedSharedComponent = indexInTypeArray - srcArchetype->FirstSharedComponent; - BuildSharedComponentIndicesWithRemovedComponent(indexOfRemovedSharedComponent, dstArchetype->NumSharedComponents, srcChunk->SharedComponentValues, archetypeChunkFilter.SharedComponentValues); + BuildSharedComponentIndicesWithRemovedComponent(indexOfRemovedSharedComponent, dstArchetype->NumSharedComponents, srcSharedComponentValues, archetypeChunkFilter.SharedComponentValues); } else { for (int i = 0; i < srcArchetype->NumSharedComponents; i++) - archetypeChunkFilter.SharedComponentValues[i] = srcChunk->SharedComponentValues[i]; + archetypeChunkFilter.SharedComponentValues[i] = srcSharedComponentValues[i]; } return archetypeChunkFilter; } - ArchetypeChunkFilter GetArchetypeChunkFilterWithRemovedComponent(Chunk* srcChunk, ComponentType componentType) + ArchetypeChunkFilter GetArchetypeChunkFilterWithRemovedComponent(ChunkIndex srcChunk, ComponentType componentType) { - var srcArchetype = srcChunk->Archetype; + var srcArchetype = GetArchetype(srcChunk); int indexInTypeArray = 0; var dstArchetype = GetArchetypeWithRemovedComponent(srcArchetype, componentType, &indexInTypeArray); if (dstArchetype == srcArchetype) @@ -373,20 +375,21 @@ ArchetypeChunkFilter GetArchetypeChunkFilterWithRemovedComponent(Chunk* srcChunk return GetArchetypeChunkFilterWithRemovedComponent(srcChunk, dstArchetype, indexInTypeArray, componentType); } - ArchetypeChunkFilter GetArchetypeChunkFilterWithRemovedComponents(Chunk* srcChunk, Archetype* dstArchetype) + ArchetypeChunkFilter GetArchetypeChunkFilterWithRemovedComponents(ChunkIndex srcChunk, Archetype* dstArchetype) { - var srcArchetype = srcChunk->Archetype; + var srcArchetype = GetArchetype(srcChunk); var archetypeChunkFilter = new ArchetypeChunkFilter(); archetypeChunkFilter.Archetype = dstArchetype; var numSrcSharedComponents = srcArchetype->NumSharedComponents; + var srcSharedComponentValues = srcArchetype->Chunks.GetSharedComponentValues(srcChunk.ListIndex); if (dstArchetype->NumSharedComponents < numSrcSharedComponents) { - BuildSharedComponentIndicesWithRemovedComponents(srcArchetype, dstArchetype, srcChunk->SharedComponentValues, archetypeChunkFilter.SharedComponentValues); + BuildSharedComponentIndicesWithRemovedComponents(srcArchetype, dstArchetype, srcSharedComponentValues, archetypeChunkFilter.SharedComponentValues); } else { for (int i = 0; i < numSrcSharedComponents; i++) - archetypeChunkFilter.SharedComponentValues[i] = srcChunk->SharedComponentValues[i]; + archetypeChunkFilter.SharedComponentValues[i] = srcSharedComponentValues[i]; } return archetypeChunkFilter; } diff --git a/Unity.Entities/EntityComponentStoreCreateDestroyEntities.cs b/Unity.Entities/EntityComponentStoreCreateDestroyEntities.cs index 47642b3..151b830 100644 --- a/Unity.Entities/EntityComponentStoreCreateDestroyEntities.cs +++ b/Unity.Entities/EntityComponentStoreCreateDestroyEntities.cs @@ -2,7 +2,6 @@ using System.Runtime.CompilerServices; using Unity.Assertions; using Unity.Burst; -using Unity.Burst.CompilerServices; using Unity.Burst.Intrinsics; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; @@ -25,9 +24,10 @@ public void CreateEntities(Archetype* archetype, Entity* entities, int count) while (count != 0) { var chunk = GetChunkWithEmptySlots(ref archetypeChunkFilter); - var allocateCount = math.min(count, chunk->UnusedCount); + var unusedCount = archetype->ChunkCapacity - chunk.Count; + var allocateCount = math.min(count, unusedCount); - ChunkDataUtility.Allocate(chunk, entities, allocateCount); + ChunkDataUtility.Allocate(archetype, chunk, entities, allocateCount); count -= allocateCount; @@ -113,7 +113,7 @@ public void DestroyEntities(Entity* entities, int count) var batchCount = entityBatchInChunk.Count; var indexInChunk = entityBatchInChunk.StartIndex; - if (chunk == null) + if (chunk == ChunkIndex.Null) { entityIndex += batchCount; continue; @@ -160,7 +160,7 @@ public void DestroyEntities(Entity* entities, int count) additionalDestroyList.Dispose(); } - public Chunk* GetCleanChunkNoMetaChunk(Archetype* archetype, SharedComponentValues sharedComponentValues) + public ChunkIndex GetCleanChunkNoMetaChunk(Archetype* archetype, SharedComponentValues sharedComponentValues) { var newChunk = AllocateChunk(); ChunkDataUtility.AddEmptyChunk(archetype, newChunk, sharedComponentValues); @@ -168,7 +168,7 @@ public void DestroyEntities(Entity* entities, int count) return newChunk; } - public Chunk* GetCleanChunk(Archetype* archetype, SharedComponentValues sharedComponentValues) + public ChunkIndex GetCleanChunk(Archetype* archetype, SharedComponentValues sharedComponentValues) { var newChunk = AllocateChunk(); ChunkDataUtility.AddEmptyChunk(archetype, newChunk, sharedComponentValues); @@ -210,13 +210,17 @@ internal void DestroyMetaChunkEntity(Entity entity) DestroyEntities(&entity, 1); } - internal void CreateMetaEntityForChunk(Chunk* chunk) + internal void CreateMetaEntityForChunk(ChunkIndex chunk) { fixed(EntityComponentStore* entityComponentStore = &this) { - CreateEntities(chunk->Archetype->MetaChunkArchetype, &chunk->metaChunkEntity, 1); + var archetype = entityComponentStore->GetArchetype(chunk); + var metaEntity = Entity.Null; + CreateEntities(archetype->MetaChunkArchetype, &metaEntity, 1); - var chunkHeader = (ChunkHeader*)GetComponentDataWithTypeRW(chunk->metaChunkEntity, m_ChunkHeaderType, GlobalSystemVersion); + chunk.MetaChunkEntity = metaEntity; + + var chunkHeader = (ChunkHeader*)GetComponentDataWithTypeRW(metaEntity, m_ChunkHeaderType, GlobalSystemVersion); chunkHeader->ArchetypeChunk = new ArchetypeChunk(chunk, entityComponentStore); } @@ -224,20 +228,21 @@ internal void CreateMetaEntityForChunk(Chunk* chunk) struct InstantiateRemapChunk { - public Chunk* Chunk; + public ChunkIndex Chunk; public int IndexInChunk; public int AllocatedCount; public int InstanceBeginIndex; } - void AddToDestroyList(Chunk* chunk, int indexInChunk, int batchCount, int inputDestroyCount, + void AddToDestroyList(ChunkIndex chunk, int indexInChunk, int batchCount, int inputDestroyCount, ref UnsafeList entitiesList, ref int minBufferLength, ref int maxBufferLength) { - int indexInArchetype = ChunkDataUtility.GetIndexInTypeArray(chunk->Archetype, m_LinkedGroupType); + var archetype = GetArchetype(chunk); + int indexInArchetype = ChunkDataUtility.GetIndexInTypeArray(archetype, m_LinkedGroupType); if (indexInArchetype != -1) { - var baseHeader = ChunkDataUtility.GetComponentDataWithTypeRO(chunk, indexInChunk, m_LinkedGroupType); - var stride = chunk->Archetype->SizeOfs[indexInArchetype]; + var baseHeader = ChunkDataUtility.GetComponentDataWithTypeRO(chunk, archetype, indexInChunk, m_LinkedGroupType); + var stride = archetype->SizeOfs[indexInArchetype]; for (int i = 0; i != batchCount; i++) { var header = (BufferHeader*)(baseHeader + stride * i); @@ -261,11 +266,11 @@ void AddToDestroyList(Chunk* chunk, int indexInChunk, int batchCount, int inputD void DestroyBatch(in EntityBatchInChunk batch) { var chunk = batch.Chunk; - var archetype = chunk->Archetype; + var archetype = GetArchetype(chunk); if (!archetype->CleanupNeeded) { - ChunkDataUtility.Deallocate(batch); + ChunkDataUtility.Deallocate(archetype, batch); } else { @@ -279,16 +284,17 @@ void DestroyBatch(in EntityBatchInChunk batch) var dstArchetypeChunkFilter = new ArchetypeChunkFilter(); dstArchetypeChunkFilter.Archetype = cleanupResidueArchetype; + var srcSharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); if (RequiresBuildingResidueSharedComponentIndices(archetype, dstArchetypeChunkFilter.Archetype)) { - BuildResidueSharedComponentIndices(archetype, dstArchetypeChunkFilter.Archetype, chunk->SharedComponentValues, dstArchetypeChunkFilter.SharedComponentValues); + BuildResidueSharedComponentIndices(archetype, dstArchetypeChunkFilter.Archetype, srcSharedComponentValues, dstArchetypeChunkFilter.SharedComponentValues); } else { - chunk->SharedComponentValues.CopyTo(dstArchetypeChunkFilter.SharedComponentValues, 0, archetype->NumSharedComponents); + srcSharedComponentValues.CopyTo(dstArchetypeChunkFilter.SharedComponentValues, 0, archetype->NumSharedComponents); } - if (count == chunk->Count) + if (count == chunk.Count) Move(chunk, ref dstArchetypeChunkFilter); else Move(new EntityBatchInChunk {Chunk = chunk, StartIndex = startIndex, Count = count}, ref dstArchetypeChunkFilter); @@ -321,31 +327,33 @@ void BuildResidueSharedComponentIndices(Archetype* srcArchetype, Archetype* dstA int InstantiateEntitiesOne(Entity srcEntity, Entity* outputEntities, int instanceCount, InstantiateRemapChunk* remapChunks, int remapChunksCount, bool removePrefab) { var src = GetEntityInChunk(srcEntity); - var srcArchetype = src.Chunk->Archetype; + var srcArchetype = GetArchetype(src.Chunk); var dstArchetype = removePrefab ? srcArchetype->InstantiateArchetype : srcArchetype->CopyArchetype; var archetypeChunkFilter = new ArchetypeChunkFilter(); archetypeChunkFilter.Archetype = dstArchetype; + var srcSharedComponentValues = srcArchetype->Chunks.GetSharedComponentValues(src.Chunk.ListIndex); if (RequiresBuildingResidueSharedComponentIndices(srcArchetype, dstArchetype)) { - BuildResidueSharedComponentIndices(srcArchetype, dstArchetype, src.Chunk->SharedComponentValues, archetypeChunkFilter.SharedComponentValues); + BuildResidueSharedComponentIndices(srcArchetype, dstArchetype, srcSharedComponentValues, archetypeChunkFilter.SharedComponentValues); } else { // Always copy shared component indices since GetChunkWithEmptySlots might reallocate the storage of SharedComponentValues - src.Chunk->SharedComponentValues.CopyTo(archetypeChunkFilter.SharedComponentValues, 0, dstArchetype->NumSharedComponents); + srcSharedComponentValues.CopyTo(archetypeChunkFilter.SharedComponentValues, 0, dstArchetype->NumSharedComponents); } int instanceBeginIndex = 0; while (instanceBeginIndex != instanceCount) { var chunk = GetChunkWithEmptySlots(ref archetypeChunkFilter); - var indexInChunk = chunk->Count; - var allocateCount = math.min(instanceCount - instanceBeginIndex, chunk->UnusedCount); + var indexInChunk = chunk.Count; + var unusedCount = dstArchetype->ChunkCapacity - indexInChunk; + var allocateCount = math.min(instanceCount - instanceBeginIndex, unusedCount); - ChunkDataUtility.AllocateClone(chunk, outputEntities + instanceBeginIndex, allocateCount, srcEntity); + ChunkDataUtility.AllocateClone(dstArchetype, chunk, outputEntities + instanceBeginIndex, allocateCount, srcEntity); if (remapChunks != null) { @@ -419,7 +427,7 @@ void InstantiateEntitiesGroup(Entity* srcEntities, int srcEntityCount, Entity* o for (int i = 0; i != remapChunksCount; i++) { var chunk = remapChunks[i].Chunk; - var dstArchetype = chunk->Archetype; + var dstArchetype = GetArchetype(chunk); var allocatedCount = remapChunks[i].AllocatedCount; var indexInChunk = remapChunks[i].IndexInChunk; var instanceBeginIndex = remapChunks[i].InstanceBeginIndex; @@ -428,7 +436,7 @@ void InstantiateEntitiesGroup(Entity* srcEntities, int srcEntityCount, Entity* o EntityRemapUtility.PatchEntitiesForPrefab(dstArchetype->ScalarEntityPatches + 1, dstArchetype->ScalarEntityPatchCount - 1, dstArchetype->BufferEntityPatches, dstArchetype->BufferEntityPatchCount, - chunk->Buffer, indexInChunk, allocatedCount, srcEntities, localRemap, srcEntityCount); + chunk.Buffer, indexInChunk, allocatedCount, srcEntities, localRemap, srcEntityCount); if (dstArchetype->HasManagedEntityRefs) { @@ -453,7 +461,7 @@ EntityBatchInChunk GetFirstEntityBatchInChunk(Entity* entities, int count) var chunk = versions[baseEntityIndex] == entities[0].Version ? m_EntityInChunkByEntity[baseEntityIndex].Chunk - : null; + : ChunkIndex.Null; var indexInChunk = chunkData[baseEntityIndex].IndexInChunk; var batchCount = 0; @@ -470,7 +478,7 @@ EntityBatchInChunk GetFirstEntityBatchInChunk(Entity* entities, int count) } else { - if (chunk != null) + if (chunk != ChunkIndex.Null) break; } @@ -548,8 +556,8 @@ public void Execute() } // It is now a valid entity, but version has changed - if (entityInChunkByEntity[i].Chunk != null && - !entityInChunkByEntity[i].Chunk->Archetype->HasChunkHeader) + if (entityInChunkByEntity[i].Chunk != ChunkIndex.Null && + !Store->GetArchetype(entityInChunkByEntity[i].Chunk)->HasChunkHeader) { CreatedEntities.Add(new Entity { Index = i, Version = versionByEntity[i] }); state[i] = versionByEntity[i]; diff --git a/Unity.Entities/EntityComponentStoreDebug.cs b/Unity.Entities/EntityComponentStoreDebug.cs index 66645e6..bd72827 100644 --- a/Unity.Entities/EntityComponentStoreDebug.cs +++ b/Unity.Entities/EntityComponentStoreDebug.cs @@ -1,6 +1,6 @@ using System; -using System.Collections.Generic; using System.Diagnostics; +using System.Runtime.CompilerServices; using Unity.Assertions; using Unity.Burst; using Unity.Burst.CompilerServices; @@ -54,28 +54,30 @@ public void CheckInternalConsistency(object[] managedComponentData) } var countInArchetype = 0; + var capacity = archetype->ChunkCapacity; for (var j = 0; j < archetype->Chunks.Count; ++j) { var chunk = archetype->Chunks[j]; - Assert.IsTrue(chunk->Archetype == archetype, "chunk belongs to incorrect archetype"); - Assert.IsTrue(chunk->Capacity >= chunk->Count, "chunk entity count exceeds chunk capacity"); - Assert.AreEqual(chunk->Count, archetype->Chunks.GetChunkEntityCount(j), "cached chunk entity count in archetype does not match actual count"); - Assert.AreNotEqual(0, chunk->Count, "found chunk with entity count of 0; this should never happen"); + Assert.IsTrue(selfPtr->GetArchetype(chunk) == archetype, "chunk belongs to incorrect archetype"); + var chunkCount = chunk.Count; + Assert.IsTrue(capacity >= chunkCount, "chunk entity count exceeds chunk capacity"); + Assert.AreEqual(chunkCount, archetype->Chunks.GetChunkEntityCount(j), "cached chunk entity count in archetype does not match actual count"); + Assert.AreNotEqual(0, chunkCount, "found chunk with entity count of 0; this should never happen"); - var chunkEntities = (Entity*)chunk->Buffer; - AssertEntitiesExist(chunkEntities, chunk->Count); + var chunkEntities = (Entity*)chunk.Buffer; + AssertEntitiesExist(chunkEntities, chunkCount); - if (chunk->SequenceNumber == 0) + if (chunk.SequenceNumber == 0) throw new ArgumentException("Sequence number must not be 0"); - if (!usedSequenceNumbers.Add(chunk->SequenceNumber)) + if (!usedSequenceNumbers.Add(chunk.SequenceNumber)) throw new ArgumentException("Sequence number must be unique"); - if (chunk->Count < chunk->Capacity) + if (chunkCount < capacity) { if (archetype->NumSharedComponents == 0) { - Assert.IsTrue(chunk->ListWithEmptySlotsIndex >= 0 && chunk->ListWithEmptySlotsIndex < archetype->ChunksWithEmptySlots.Length, "chunk with empty slots is not tracked correctly"); - Assert.IsTrue(chunk == archetype->ChunksWithEmptySlots.Ptr[chunk->ListWithEmptySlotsIndex]); + Assert.IsTrue(chunk.ListWithEmptySlotsIndex >= 0 && chunk.ListWithEmptySlotsIndex < archetype->ChunksWithEmptySlots.Length, "chunk with empty slots is not tracked correctly"); + Assert.IsTrue(chunk == archetype->ChunksWithEmptySlots[chunk.ListWithEmptySlotsIndex]); } else { @@ -83,32 +85,33 @@ public void CheckInternalConsistency(object[] managedComponentData) } } - countInArchetype += chunk->Count; + countInArchetype += chunkCount; - if (chunk->Archetype->HasChunkHeader) // Chunk entities with chunk components are not supported + if (archetype->HasChunkHeader) // Chunk entities with chunk components are not supported { - Assert.IsFalse(chunk->Archetype->HasChunkComponents, "chunks with a chunk header should not have chunk components"); + Assert.IsFalse(archetype->HasChunkComponents, "chunks with a chunk header should not have chunk components"); } - Assert.AreEqual(chunk->Archetype->HasChunkComponents, chunk->metaChunkEntity != Entity.Null, $"expected hasChunkComponents={archetype->HasChunkComponents}, actual={!archetype->HasChunkComponents}"); - if (chunk->metaChunkEntity != Entity.Null) + var metaChunkEntity = chunk.MetaChunkEntity; + Assert.AreEqual(archetype->HasChunkComponents, metaChunkEntity != Entity.Null, $"expected hasChunkComponents={archetype->HasChunkComponents}, actual={!archetype->HasChunkComponents}"); + if (metaChunkEntity != Entity.Null) { var chunkHeaderTypeIndex = TypeManager.GetTypeIndex(); - AssertEntitiesExist(&chunk->metaChunkEntity, 1); - AssertEntityHasComponent(chunk->metaChunkEntity, chunkHeaderTypeIndex); + AssertEntityExists(metaChunkEntity); + AssertEntityHasComponent(metaChunkEntity, chunkHeaderTypeIndex); var chunkHeader = - *(ChunkHeader*)GetComponentDataWithTypeRO(chunk->metaChunkEntity, + *(ChunkHeader*)GetComponentDataWithTypeRO(metaChunkEntity, chunkHeaderTypeIndex); Assert.IsTrue(chunk == chunkHeader.ArchetypeChunk.m_Chunk, "chunk's metaChunkEntity chunk != chunk's metaChunk"); - var metaChunk = GetChunk(chunk->metaChunkEntity); - Assert.IsTrue(metaChunk->Archetype == chunk->Archetype->MetaChunkArchetype, "chunk's metaChunk archetype doesn't match cached value"); + var metaChunk = GetChunk(metaChunkEntity); + Assert.IsTrue(selfPtr->GetArchetype(metaChunk) == selfPtr->GetArchetype(chunk)->MetaChunkArchetype, "chunk's metaChunk archetype doesn't match cached value"); Assert.IsTrue(chunkHeader.ArchetypeChunk.m_EntityComponentStore == selfPtr, "metaChunkEntity's ArchetypeChunk has incorrect EntityComponentStore"); } for (int iType = managedTypeBegin; iType < managedTypeEnd; ++iType) { - var managedIndicesInChunk = (int*)(chunk->Buffer + archetype->Offsets[iType]); - for (int ie = 0; ie < chunk->Count; ++ie) + var managedIndicesInChunk = (int*)(chunk.Buffer + archetype->Offsets[iType]); + for (int ie = 0, count = chunkCount; ie < count; ++ie) { var index = managedIndicesInChunk[ie]; if (index == 0) @@ -151,13 +154,13 @@ public void CheckInternalConsistency(object[] managedComponentData) managedComponentIndices.Dispose(); // Iterate by free list - Assert.IsTrue(m_EntityInChunkByEntity[m_NextFreeEntityIndex].Chunk == null, "free chunk list does not end in a null chunk"); + Assert.IsTrue(m_EntityInChunkByEntity[m_NextFreeEntityIndex].Chunk == ChunkIndex.Null, "free chunk list does not end in a null chunk"); var entityCountByFreeList = EntitiesCapacity; int freeIndex = m_NextFreeEntityIndex; while (freeIndex != -1) { - Assert.IsTrue(m_EntityInChunkByEntity[freeIndex].Chunk == null, "found non=null chunk in free list"); + Assert.IsTrue(m_EntityInChunkByEntity[freeIndex].Chunk == ChunkIndex.Null, "found non=null chunk in free list"); Assert.IsTrue(freeIndex < EntitiesCapacity, "free entity index exceeds capacity"); freeIndex = m_EntityInChunkByEntity[freeIndex].IndexInChunk; @@ -171,15 +174,15 @@ public void CheckInternalConsistency(object[] managedComponentData) for (var i = 0; i != EntitiesCapacity; i++) { var chunk = m_EntityInChunkByEntity[i].Chunk; - if (chunk == null) + if (chunk == ChunkIndex.Null) continue; + var archetype = selfPtr->GetArchetype(chunk); + entityCountByEntities++; - var archetype = m_ArchetypeByEntity[i]; - Assert.AreEqual((IntPtr)archetype, (IntPtr)chunk->Archetype, "entity's archetype does not match entity's chunk's archetype"); - Assert.AreEqual(entityType, archetype->Types[0].TypeIndex, "found archetype with types[0] != Entity"); - Assert.IsTrue(m_EntityInChunkByEntity[i].IndexInChunk < m_EntityInChunkByEntity[i].Chunk->Count, "found entity with invalid indexInChunk"); - var entity = *(Entity*)ChunkDataUtility.GetComponentDataRO(m_EntityInChunkByEntity[i].Chunk, + Assert.AreEqual(entityType, selfPtr->GetArchetype(chunk)->Types[0].TypeIndex, "found archetype with types[0] != Entity"); + Assert.IsTrue(m_EntityInChunkByEntity[i].IndexInChunk < m_EntityInChunkByEntity[i].Chunk.Count, "found entity with invalid indexInChunk"); + var entity = *(Entity*)ChunkDataUtility.GetComponentDataRO(m_EntityInChunkByEntity[i].Chunk, archetype, m_EntityInChunkByEntity[i].IndexInChunk, 0); Assert.AreEqual(i, entity.Index, "found entity with invalid index"); Assert.AreEqual(m_VersionByEntity[i], entity.Version, "found entity with invalid version"); @@ -207,7 +210,7 @@ public static void AssertAllEntitiesCopied(EntityComponentStore* lhs, EntityComp int capacity = lhs->EntitiesCapacity; for (int i = 0; i != capacity; i++) { - if (lhsEntities[i].Chunk == null && rhsEntities[i].Chunk == null) + if (lhsEntities[i].Chunk == ChunkIndex.Null && rhsEntities[i].Chunk == ChunkIndex.Null) continue; if (lhsEntities[i].IndexInChunk != rhsEntities[i].IndexInChunk) @@ -219,10 +222,10 @@ public static void AssertAllEntitiesCopied(EntityComponentStore* lhs, EntityComp [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] public void ValidateEntity(Entity entity) { - if (Hint.Unlikely(entity.Index < 0)) + if (entity.Index < 0) throw new ArgumentException( $"All entities created using EntityCommandBuffer.CreateEntity must be realized via playback(). One of the entities is still deferred (Index: {entity.Index})."); - if (Hint.Unlikely((uint)entity.Index >= (uint)EntitiesCapacity)) + if ((uint)entity.Index >= (uint)EntitiesCapacity) throw new ArgumentException( "An Entity index is larger than the capacity of the EntityManager. This means the entity was created by a different world or the entity.Index got corrupted or incorrectly assigned and it may not be used on this EntityManager."); } @@ -265,11 +268,18 @@ public void AssertCanCreateArchetype(ComponentType* componentTypes, int componen var componentInstanceSize = GetComponentArraySize(componentTypeInfo.SizeInChunk, 1); archetypeInstanceSize += componentInstanceSize; } - var chunkDataSize = Chunk.GetChunkBufferSize(); + var chunkDataSize = Chunk.kChunkBufferSize; if (archetypeInstanceSize > chunkDataSize) throw new ArgumentException($"Archetype too large to fit in chunk. Instance size {archetypeInstanceSize} bytes. Maximum chunk size {chunkDataSize}. Components in new archetype: {AggregateNewArchetypesComponentTypes(componentTypes, componentTypeCount)}"); } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public void AssertEntityExists(Entity entity) + { + AssertEntitiesExist(&entity, 1); + } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] public void AssertEntitiesExist(Entity* entities, int count) { @@ -281,10 +291,12 @@ public void AssertEntitiesExist(Entity* entities, int count) int index = entity->Index; var exists = m_VersionByEntity[index] == entity->Version && - m_EntityInChunkByEntity[index].Chunk != null; + m_EntityInChunkByEntity[index].Chunk != ChunkIndex.Null; if (!exists) throw new ArgumentException( - "All entities passed to EntityManager must exist. One of the entities has already been destroyed or was never created." + AppendDestroyedEntityRecordError(*entity)); + "An EntityManager command is operating on an invalid entity. This usually means that the Entity has already been destroyed or was never created." + + " This can happen when a subscene is opened or closed between when a EntityCommandBuffer has been recorded and played back. This can invalidate recorded Entities." + + AppendDestroyedEntityRecordError(*entity)); } } @@ -368,20 +380,17 @@ public void AssertComponentIsUnmanaged(TypeIndex componentTypeIndex) [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] public void AssertCanAddComponent(Archetype* archetype, ComponentType componentType) { - if (Hint.Unlikely(componentType == m_EntityComponentType)) + if (componentType == m_EntityComponentType) throw new ArgumentException("Cannot add Entity as a component."); - if (ChunkDataUtility.GetIndexInTypeArray(archetype, componentType.TypeIndex) != -1) - return; // archetype already has the component, so "add" will be a no-op. - - if (Hint.Unlikely(componentType.IsSharedComponent && (archetype->NumSharedComponents == kMaxSharedComponentCount))) + if (componentType.IsSharedComponent && (archetype->NumSharedComponents == kMaxSharedComponentCount)) throw new InvalidOperationException($"Cannot add more than {kMaxSharedComponentCount} SharedComponent's to a single Archetype. Attempting to add '{componentType}'. Archetype already contains types ({AggregateArchetypeComponentTypes(archetype)})."); var componentTypeInfo = GetTypeInfo(componentType.TypeIndex); var componentInstanceSize = GetComponentArraySize(componentTypeInfo.SizeInChunk, 1); var archetypeInstanceSize = archetype->InstanceSizeWithOverhead + componentInstanceSize; - var chunkDataSize = Chunk.GetChunkBufferSize(); - if (Hint.Unlikely(archetypeInstanceSize > chunkDataSize)) + var chunkDataSize = Chunk.kChunkBufferSize; + if (archetypeInstanceSize > chunkDataSize) throw new InvalidOperationException($"Entity archetype component data is too large. Previous archetype size per instance {archetype->InstanceSizeWithOverhead} bytes. Attempting to add component '{componentType}' with size {componentInstanceSize} bytes. Maximum chunk size {chunkDataSize}."); } @@ -405,7 +414,7 @@ public void AssertCanAddComponents(Archetype* archetype, in ComponentTypeSet com throw new InvalidOperationException($"Cannot add more than {kMaxSharedComponentCount} SharedComponent's to a single Archetype. Attempting to add types {AggregateComponentTypes(componentTypeSet)}. Archetype already contains types ({AggregateArchetypeComponentTypes(archetype)})."); var archetypeInstanceSize = archetype->InstanceSizeWithOverhead + totalComponentInstanceSize; - var chunkDataSize = Chunk.GetChunkBufferSize(); + var chunkDataSize = Chunk.kChunkBufferSize; if (archetypeInstanceSize > chunkDataSize) throw new InvalidOperationException($"Entity archetype component data is too large. Previous archetype size per instance {archetype->InstanceSizeWithOverhead} bytes. Attempting to add multiple components ({AggregateComponentTypes(componentTypeSet)}) with a combined size {totalComponentInstanceSize} bytes. Maximum chunk size {chunkDataSize}. Archetype already contains types ({AggregateArchetypeComponentTypes(archetype)})."); } @@ -498,8 +507,8 @@ public void AssertCanAddComponents(UnsafeMatchingArchetypePtrList archetypeList, var archetype = ptrs[i]->Archetype; if ((archetype->NumSharedComponents + newShared) > kMaxSharedComponentCount) throw new InvalidOperationException($"Cannot add more than {kMaxSharedComponentCount} SharedComponent's to a single Archetype. Attempting to add types {AggregateComponentTypes(componentTypeSet)}. Archetype already contains types ({AggregateArchetypeComponentTypes(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 components {AggregateComponentTypes(componentTypeSet)} with total size {totalNewComponentSize} bytes. Maximum chunk size {Chunk.GetChunkBufferSize()}. Archetype already contains types ({AggregateArchetypeComponentTypes(archetype)})."); + if ((archetype->InstanceSizeWithOverhead + totalNewComponentSize) > Chunk.kChunkBufferSize) + throw new InvalidOperationException($"Entity archetype component data is too large. Previous archetype size per instance {archetype->InstanceSizeWithOverhead} bytes. Attempting to add components {AggregateComponentTypes(componentTypeSet)} with total size {totalNewComponentSize} bytes. Maximum chunk size {Chunk.kChunkBufferSize}. Archetype already contains types ({AggregateArchetypeComponentTypes(archetype)})."); } } @@ -538,7 +547,7 @@ public void AssertCanAddComponent(NativeList batches, Compon Archetype* archetype = null; for (int i = 0; i < batches.Length; i++) { - var nextArchetype = batches[i].Chunk->Archetype; + var nextArchetype = GetArchetype(batches[i].Chunk); if (nextArchetype != archetype) { if (archetype != null) @@ -562,7 +571,6 @@ public void AssertCanRemoveComponents(in ComponentTypeSet typeSet) AssertCanRemoveComponent(ComponentType.FromTypeIndex(typeSet.GetTypeIndex(i))); } - [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] void AssertWillDestroyAllInLinkedEntityGroup(UnsafeList chunksToProcess, ref BufferTypeHandle linkedGroupTypeHandle, bool queryHasEnableableComponents) @@ -570,10 +578,10 @@ void AssertWillDestroyAllInLinkedEntityGroup(UnsafeList chu var sortedChunksToProcess = new UnsafeList(chunksToProcess.Length, Allocator.Temp); sortedChunksToProcess.CopyFrom(chunksToProcess); sortedChunksToProcess.Sort(); - var sortedChunkPtrs = new UnsafeList(sortedChunksToProcess.Length, Allocator.Temp); + var sortedChunkPtrs = new UnsafeList(sortedChunksToProcess.Length, Allocator.Temp); for (int i = 0, chunkCount = sortedChunksToProcess.Length; i < chunkCount; ++i) { - sortedChunkPtrs.AddNoResize((ulong)sortedChunksToProcess[i].Chunk); + sortedChunkPtrs.AddNoResize(sortedChunksToProcess[i].Chunk); } fixed (EntityComponentStore* pThis = &this) @@ -596,7 +604,7 @@ void AssertWillDestroyAllInLinkedEntityGroup(UnsafeList chu if (!Exists(referencedEntity)) continue; var referencedEntityInChunk = GetEntityInChunk(referencedEntity); - int sortedChunkIndex = sortedChunkPtrs.BinarySearch((ulong)referencedEntityInChunk.Chunk); + int sortedChunkIndex = sortedChunkPtrs.BinarySearch(referencedEntityInChunk.Chunk); if (Hint.Likely(sortedChunkIndex >= 0)) { // referencedEntity's chunk matches the query. @@ -608,7 +616,7 @@ void AssertWillDestroyAllInLinkedEntityGroup(UnsafeList chu if (Hint.Unlikely((shiftedMask.ULong0 & 0x1) == 0)) { // referencedChunk matches the query, but referencedEntity is not enabled in this query & will not be deleted - var chunkEntities = (Entity*)chunksToProcess[i].Chunk->Buffer; + var chunkEntities = (Entity*)chunksToProcess[i].Chunk.Buffer; ThrowDestroyEntityError(chunkEntities[b], referencedEntity); } } @@ -616,7 +624,7 @@ void AssertWillDestroyAllInLinkedEntityGroup(UnsafeList chu else { // referencedChunk isn't in chunksToProcess at all, and won't be destroyed. - var chunkEntities = (Entity*)chunksToProcess[i].Chunk->Buffer; + var chunkEntities = (Entity*)chunksToProcess[i].Chunk.Buffer; ThrowDestroyEntityError(chunkEntities[b], referencedEntity); } } @@ -684,7 +692,8 @@ public void CheckCanAddChunkComponent(NativeArray chunkArray, Co for (int i = 0; i < chunkArray.Length; ++i) { var chunk = chunks[i].m_Chunk; - if (ChunkDataUtility.GetIndexInTypeArray(chunk->Archetype, componentType.TypeIndex) != -1) + var archetype = chunks[i].Archetype.Archetype; + if (ChunkDataUtility.GetIndexInTypeArray(archetype, componentType.TypeIndex) != -1) { result = false; return; @@ -692,12 +701,6 @@ public void CheckCanAddChunkComponent(NativeArray chunkArray, Co } } - [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] - 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"), Conditional("UNITY_DOTS_DEBUG")] public void AssertCanInstantiateEntities(Entity srcEntity, Entity* outputEntities, int instanceCount) { @@ -799,11 +802,11 @@ public static void AssertPaddingBitsAreZeroForArchetype(Archetype* archetype) int memoryOrderIndexInArchetype = archetype->TypeIndexInArchetypeToMemoryOrderIndex[typeIndexInArchetype]; v128 mask = *archetype->Chunks.GetComponentEnabledMaskArrayForTypeInChunk(memoryOrderIndexInArchetype, chunkIndex); - var paddingBits = EnabledBitUtility.ShiftRight(mask, chunk->Count); + var paddingBits = EnabledBitUtility.ShiftRight(mask, chunk.Count); int enabledCount = EnabledBitUtility.countbits(paddingBits); if (enabledCount != 0) Assert.AreEqual(0, enabledCount, - $"enabled bits padding check failed: chunk={chunkIndex} memoryOrderIndex={memoryOrderIndexInArchetype} mask={mask.ULong1:X16}:{mask.ULong0:X16} entityCount={chunk->Count} archetype={*archetype}"); + $"enabled bits padding check failed: chunk={chunkIndex} memoryOrderIndex={memoryOrderIndexInArchetype} mask={mask.ULong1:X16}:{mask.ULong0:X16} entityCount={chunk.Count} archetype={*archetype}"); } } } @@ -821,10 +824,10 @@ public static void AssertEnabledBitsHierarchyIsCorrectForArchetype(Archetype* ar int memoryOrderIndexInArchetype = archetype->TypeIndexInArchetypeToMemoryOrderIndex[typeIndexInArchetype]; v128 mask = *archetype->Chunks.GetComponentEnabledMaskArrayForTypeInChunk(memoryOrderIndexInArchetype, chunkIndex); int storedDisabledCount = archetype->Chunks.GetChunkDisabledCountForType(memoryOrderIndexInArchetype, chunkIndex); - int actualDisabledCount = chunk->Count - EnabledBitUtility.countbits(mask); + int actualDisabledCount = chunk.Count - EnabledBitUtility.countbits(mask); if (actualDisabledCount != storedDisabledCount) Assert.AreEqual(actualDisabledCount, storedDisabledCount, - $"enabled bits hierarchical mismatch: chunk={chunkIndex} memoryOrderIndex={memoryOrderIndexInArchetype} mask={mask.ULong1:X16}:{mask.ULong0:X16} entityCount={chunk->Count} storedCount={storedDisabledCount} archetype={*archetype}"); + $"enabled bits hierarchical mismatch: chunk={chunkIndex} memoryOrderIndex={memoryOrderIndexInArchetype} mask={mask.ULong1:X16}:{mask.ULong0:X16} entityCount={chunk.Count} storedCount={storedDisabledCount} archetype={*archetype}"); } } } diff --git a/Unity.Entities/EntityDataAccess.cs b/Unity.Entities/EntityDataAccess.cs index 19e6a4c..f80c8e6 100644 --- a/Unity.Entities/EntityDataAccess.cs +++ b/Unity.Entities/EntityDataAccess.cs @@ -96,9 +96,7 @@ unsafe struct EntityDataAccess : IDisposable // copies of the root struct when inspecting, and it generally // misbehaves from there. -#if !NET_DOTS [DebuggerBrowsable(DebuggerBrowsableState.Never)] -#endif internal EntityComponentStore* EntityComponentStore { // This is always safe as the EntityDataAccess is always unsafe heap allocated. @@ -111,9 +109,7 @@ internal EntityComponentStore* EntityComponentStore } } -#if !NET_DOTS [DebuggerBrowsable(DebuggerBrowsableState.Never)] -#endif internal EntityQueryManager* EntityQueryManager { // This is always safe as the EntityDataAccess is always unsafe heap allocated. @@ -126,9 +122,7 @@ internal EntityQueryManager* EntityQueryManager } } -#if !NET_DOTS [DebuggerBrowsable(DebuggerBrowsableState.Never)] -#endif internal ComponentDependencyManager* DependencyManager { // This is always safe as the EntityDataAccess is always unsafe heap allocated. @@ -1923,13 +1917,9 @@ public void SetSharedComponentDataBoxedDefaultMustBeNullDuringStructuralChange(E } else { -#if !UNITY_DOTSRUNTIME var componentDataAddr = (byte*)UnsafeUtility.PinGCObjectAndGetAddress(componentData, out var gcHandle) + TypeManager.ObjectOffset; newSharedComponentDataIndex = EntityComponentStore->InsertSharedComponent_Unmanaged(typeIndex, hashCode, componentDataAddr, null); UnsafeUtility.ReleaseGCObject(gcHandle); -#else - throw new NotSupportedException("This API is not supported when called with unmanaged shared component on DOTS Runtime"); -#endif } } var componentType = ComponentType.FromTypeIndex(typeIndex); @@ -2232,8 +2222,8 @@ static void BuildSharedComponentMapForMoving(ref NativeHashMap hashmap for (int i = 0; i < chunks.Length; ++i) { var chunk = chunks[i].m_Chunk; - var archetype = chunk->Archetype; - var sharedComponentValues = chunk->SharedComponentValues; + var archetype = chunks[i].Archetype.Archetype; + var sharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); for (int sharedComponentValueIndex = 0; sharedComponentValueIndex < archetype->NumSharedComponents; ++sharedComponentValueIndex) { @@ -2297,7 +2287,6 @@ public int InsertSharedComponentAssumeNonDefault(TypeIndex typeIndex, int hashCo } else { -#if !UNITY_DOTSRUNTIME /* * this is actually used in hybrid to read unmanaged shared components, but it is NOT called in dotsrt */ @@ -2305,9 +2294,6 @@ public int InsertSharedComponentAssumeNonDefault(TypeIndex typeIndex, int hashCo var index = EntityComponentStore->InsertSharedComponent_Unmanaged(typeIndex, hashCode, sharedComponentAddr, null); UnsafeUtility.ReleaseGCObject(gcHandle); return index; -#else - throw new InvalidOperationException("This API is not compatible with DotsRuntime when used with an unmanaged shared component, you must a dedicated _Unmanaged API instead."); -#endif } } @@ -2781,7 +2767,7 @@ public void SwapComponents(ArchetypeChunk leftChunk, int leftIndex, ArchetypeChu var globalSystemVersion = EntityComponentStore->GlobalSystemVersion; - ChunkDataUtility.SwapComponents(leftChunk.m_Chunk, leftIndex, rightChunk.m_Chunk, rightIndex, 1, + ChunkDataUtility.SwapComponents(leftChunk.Archetype.Archetype, leftChunk.m_Chunk, leftIndex, rightChunk.Archetype.Archetype, rightChunk.m_Chunk, rightIndex, 1, globalSystemVersion, globalSystemVersion); } diff --git a/Unity.Entities/EntityInChunk.cs b/Unity.Entities/EntityInChunk.cs index 6711568..4cdfacd 100644 --- a/Unity.Entities/EntityInChunk.cs +++ b/Unity.Entities/EntityInChunk.cs @@ -9,9 +9,9 @@ namespace Unity.Entities /// /// Represents a single entity within a chunk. Mainly used internally to sort lists of entities into chunk order. /// - public unsafe struct EntityInChunk : IComparable, IEquatable + public struct EntityInChunk : IComparable, IEquatable { - internal Chunk* Chunk; + internal ChunkIndex Chunk; internal int IndexInChunk; /// @@ -22,11 +22,9 @@ public unsafe struct EntityInChunk : IComparable, IEquatable. 0 if the two entities are equivalent. public int CompareTo(EntityInChunk other) { - ulong lhs = (ulong)Chunk; - ulong rhs = (ulong)other.Chunk; - int chunkCompare = lhs < rhs ? -1 : 1; + int chunkCompare = Chunk < other.Chunk ? -1 : 1; int indexCompare = IndexInChunk - other.IndexInChunk; - return (lhs != rhs) ? chunkCompare : indexCompare; + return Chunk != other.Chunk ? chunkCompare : indexCompare; } /// diff --git a/Unity.Entities/EntityManager.cs b/Unity.Entities/EntityManager.cs index 335a771..70c061f 100644 --- a/Unity.Entities/EntityManager.cs +++ b/Unity.Entities/EntityManager.cs @@ -89,6 +89,17 @@ private void AssertIsExclusiveTransaction() return m_EntityDataAccess; } + internal EntityDataAccess* GetCheckedEntityDataAccessExclusive() + { + if (!CanBeginExclusiveEntityTransaction()) + EndExclusiveEntityTransaction(); + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); +#endif + return m_EntityDataAccess; + } + internal EntityDataAccess* GetCheckedEntityDataAccess(SystemHandle system) { #if ENABLE_UNITY_COLLECTIONS_CHECKS @@ -102,6 +113,21 @@ private void AssertIsExclusiveTransaction() return m_EntityDataAccess; } + internal EntityDataAccess* GetCheckedEntityDataAccessExclusive(SystemHandle system) + { + if (!CanBeginExclusiveEntityTransaction()) + EndExclusiveEntityTransaction(); + +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AtomicSafetyHandle.CheckWriteAndThrow(m_Safety); +#endif +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (system.m_WorldSeqNo != m_EntityDataAccess->m_WorldUnmanaged.SequenceNumber) + throw new InvalidOperationException("System is from a different world."); +#endif + return m_EntityDataAccess; + } + internal EntityDataAccess* GetUncheckedEntityDataAccess() { return m_EntityDataAccess; @@ -239,8 +265,6 @@ internal void Initialize(World world) // call out to a user panic function, which can try to log some more information and then the native code will retry the operation. // Here we figure out if some other system was to blame, and if so we synchronize all outstanding jobs to make the downstream // systems keep running. - -#if !UNITY_DOTSRUNTIME if (JobsUtility.PanicFunction == null) { JobsUtility.PanicFunction = () => @@ -252,7 +276,6 @@ internal void Initialize(World world) } }; } -#endif #endif m_EntityDataAccess = (EntityDataAccess*)Memory.Unmanaged.Allocate(sizeof(EntityDataAccess), 16, Allocator.Persistent); UnsafeUtility.MemClear(m_EntityDataAccess, sizeof(EntityDataAccess)); @@ -450,7 +473,7 @@ public T GetChunkComponentData(ArchetypeChunk chunk) where T : unmanaged, ICo $"GetChunkComponentData<{typeName}> can not be called with an invalid archetype chunk."); } #endif - var metaChunkEntity = chunk.m_Chunk->metaChunkEntity; + var metaChunkEntity = chunk.m_Chunk.MetaChunkEntity; return GetComponentData(metaChunkEntity); } @@ -471,7 +494,7 @@ public T GetChunkComponentData(Entity entity) where T : unmanaged, IComponent var store = access->EntityComponentStore; store->AssertEntitiesExist(&entity, 1); var chunk = store->GetChunk(entity); - var metaChunkEntity = chunk->metaChunkEntity; + var metaChunkEntity = chunk.MetaChunkEntity; return access->GetComponentData(metaChunkEntity); } @@ -498,7 +521,7 @@ public void SetChunkComponentData(ArchetypeChunk chunk, T componentValue) whe $"SetChunkComponentData<{typeName}> can not be called with an invalid archetype chunk."); } #endif - var metaChunkEntity = chunk.m_Chunk->metaChunkEntity; + var metaChunkEntity = chunk.m_Chunk.MetaChunkEntity; SetComponentData(metaChunkEntity, componentValue); } @@ -776,9 +799,23 @@ public T GetUnmanagedSharedComponentData(Entity entity) where T : unmanaged, return default; } + /// + /// Retrieves the index of the managed or unmanaged shared component for an entity. + /// + /// The target entity + /// The type of the shared component to look up on the target entity + /// The index of the target entity's value for the shared component of type . + [ExcludeFromBurstCompatTesting("Accesses managed component store")] + public int GetSharedComponentIndexManaged(Entity entity) where T : struct, ISharedComponentData + { + var ecs = GetCheckedEntityDataAccess()->EntityComponentStore; + var typeIndex = TypeManager.GetTypeIndex(); + ecs->AssertEntityHasComponent(entity, typeIndex); + return ecs->GetSharedComponentDataIndex(entity, typeIndex); + } /// - /// Retrieves the index of the shared component data value for an entity. + /// Retrieves the index of the unmanaged shared component for an entity. /// /// The target entity /// The type of the unmanaged shared component to look up on the target entity @@ -792,6 +829,19 @@ public int GetSharedComponentIndex(Entity entity) where T : unmanaged, IShare return ecs->GetSharedComponentDataIndex(entity, typeIndex); } + /// + /// Retrieves the index of the unmanaged shared component data value for an entity. + /// + /// The target entity + /// The type of the unmanaged shared component to look up on the target entity + /// The index of the target entity's value for the shared component of type . + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleSharedComponentData) })] + [Obsolete("Use GetSharedComponentIndex instead. (RemovedAfter Entities 1.0) (UnityUpgradable) -> GetSharedComponentIndex(*)", true)] + public int GetSharedComponentDataIndex(Entity entity) where T : unmanaged, ISharedComponentData + { + return GetSharedComponentIndex(entity); + } + /// Obsolete. Use instead. /// The target entity /// The type of the unmanaged shared component to look up on the target entity @@ -937,21 +987,6 @@ public T GetSharedComponentManaged(Entity entity) where T : struct, ISharedCo return access->GetSharedComponentData(entity); } - /// - /// Retrieves the index of the shared component data value for an entity. - /// - /// The target entity - /// The type of the shared component to look up on the target entity - /// The index of the target entity's value for the shared component of type . - [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleSharedComponentData) })] - public int GetSharedComponentDataIndex(Entity entity) where T : unmanaged, ISharedComponentData - { - var ecs = GetCheckedEntityDataAccess()->EntityComponentStore; - var typeIndex = TypeManager.GetTypeIndex(); - ecs->AssertEntityHasComponent(entity, typeIndex); - return ecs->GetSharedComponentDataIndex(entity, typeIndex); - } - /// Obsolete. Use instead. /// The index of the shared component in the internal shared component /// list. @@ -1305,11 +1340,12 @@ DynamicBuffer GetBufferInternal(EntityDataAccess* access, Entity entity, b var safetyHandles = &access->DependencyManager->Safety; #endif - return access->GetBuffer(entity + return access->GetBuffer(entity, #if ENABLE_UNITY_COLLECTIONS_CHECKS - , safetyHandles->GetSafetyHandle(typeIndex, isReadOnly), - safetyHandles->GetBufferSafetyHandle(typeIndex), isReadOnly + safetyHandles->GetSafetyHandle(typeIndex, isReadOnly), + safetyHandles->GetBufferSafetyHandle(typeIndex), #endif + isReadOnly ); } @@ -1646,12 +1682,12 @@ private Entity CreateSingletonEntityInternal(FixedString64Bytes name = defaul public void AddComponent(EntityQuery entityQuery, ComponentType componentType) { var access = GetCheckedEntityDataAccess(); - access->AssertMainThread(); access->AssertQueryIsValid(entityQuery); var queryImpl = entityQuery._GetImpl(); if (queryImpl->IsEmptyIgnoreFilter) return; + var changes = access->BeginStructuralChanges(); access->AddComponentToQueryDuringStructuralChange(queryImpl, componentType); access->EndStructuralChanges(ref changes); @@ -2137,7 +2173,6 @@ public void RemoveComponent(Entity entity, in ComponentTypeSet componentTypeSet) public void RemoveComponent(EntityQuery entityQuery, ComponentType componentType) { var access = GetCheckedEntityDataAccess(); - access->AssertMainThread(); access->AssertQueryIsValid(entityQuery); var queryImpl = entityQuery._GetImpl(); if (queryImpl->IsEmptyIgnoreFilter) @@ -2437,38 +2472,26 @@ public void AddChunkComponentData(EntityQuery entityQuery, T componentData) w if (entityQuery.IsEmptyIgnoreFilter) return; - bool validAdd = true; - var chunks = entityQuery.ToArchetypeChunkArray(Allocator.TempJob); + using var chunks = entityQuery.ToArchetypeChunkArray(Allocator.TempJob); if (chunks.Length > 0) { - ecs->CheckCanAddChunkComponent(chunks, ComponentType.ChunkComponent(), ref validAdd); - - if (validAdd) - { - ArchetypeChunk* chunkPtr = (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks); - var componentType = ComponentType.ReadWrite(); - var componentTypeIndex = componentType.TypeIndex; - var componentTypeIndexForAdd = TypeManager.MakeChunkComponentTypeIndex(componentTypeIndex); + ArchetypeChunk* chunkPtr = (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks); + var componentType = ComponentType.ReadWrite(); + ecs->AssertCanAddComponent(entityQuery._GetImpl(), ComponentType.ChunkComponent()); + var componentTypeIndex = componentType.TypeIndex; + var componentTypeIndexForAdd = TypeManager.MakeChunkComponentTypeIndex(componentTypeIndex); #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING - // Have to record here because method is not using EntityDataAccess - if (Hint.Unlikely(ecs->m_RecordToJournal != 0)) - access->JournalAddRecord_AddComponent(default, chunkPtr, chunks.Length, &componentTypeIndexForAdd, 1); + // Have to record here because method is not using EntityDataAccess + if (Hint.Unlikely(ecs->m_RecordToJournal != 0)) + access->JournalAddRecord_AddComponent(default, chunkPtr, chunks.Length, &componentTypeIndexForAdd, 1); #endif - var changes = access->BeginStructuralChanges(); - StructuralChange.AddComponentChunks(ecs, chunkPtr, chunks.Length, componentTypeIndexForAdd); - StructuralChange.SetChunkComponent(ecs, chunkPtr, chunks.Length, &componentData, componentTypeIndex); - access->EndStructuralChanges(ref changes); - } - } - - chunks.Dispose(); - - if (!validAdd) - { - ecs->ThrowDuplicateChunkComponentError(ComponentType.ChunkComponent()); + var changes = access->BeginStructuralChanges(); + StructuralChange.AddComponentChunks(ecs, chunkPtr, chunks.Length, componentTypeIndexForAdd); + access->EndStructuralChanges(ref changes); + StructuralChange.SetChunkComponent(ecs, chunkPtr, chunks.Length, &componentData, componentTypeIndex); } } @@ -2718,7 +2741,6 @@ public void AddSharedComponent(EntityQuery entityQuery, T componentData) where T : unmanaged, ISharedComponentData { var access = GetCheckedEntityDataAccess(); - access->AssertMainThread(); access->AssertQueryIsValid(entityQuery); var queryImpl = entityQuery._GetImpl(); if (queryImpl->IsEmptyIgnoreFilter) @@ -3438,6 +3460,8 @@ public NativeArray Instantiate(Entity srcEntity, int instanceCount, Allo /// The set of entities to clone /// the set of entities that were cloned. outputEntities.Length must match srcEntities.Length [StructuralChangeMethod] + [Obsolete("This method will be removed in a future Entities release. " + + "If you wish to clone the full hierarchy of every entity in a given array, simply loop through the array to and invoke `EntityManager.Instantiate(Entity entity)` on each entity.")] public void Instantiate(NativeArray srcEntities, NativeArray outputEntities) { var access = GetCheckedEntityDataAccess(); @@ -3767,6 +3791,7 @@ public void MoveEntitiesFrom(EntityManager srcEntities, NativeArray @@ -4764,8 +4789,11 @@ void MoveEntitiesFromInternalQuery(EntityManager srcEntities, EntityQuery filter { #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG for (int i = 0; i < chunks.Length; ++i) - if (chunks[i].m_Chunk->Archetype->HasChunkHeader) + { + var archetype = srcAccess->EntityComponentStore->GetArchetype(chunks[i].m_Chunk); + if (archetype->HasChunkHeader) throw new ArgumentException("MoveEntitiesFrom can not move chunks that contain ChunkHeader components."); + } #endif var archetypeChanges = selfAccess->EntityComponentStore->BeginArchetypeChangeTracking(); @@ -4788,7 +4816,7 @@ internal void MoveEntitiesFromInternalAll(EntityManager srcEntities, NativeArray if (srcEntities.m_EntityDataAccess == m_EntityDataAccess) throw new ArgumentException("srcEntities must not be the same as this EntityManager."); - if (entityRemapping.Length < srcAccess->EntityComponentStore->EntitiesCapacity) + if (entityRemapping.Length <= srcEntities.Debug.HighestIndexEntity) throw new ArgumentException("entityRemapping.Length isn't large enough, use srcEntities.CreateEntityRemapArray"); if (!srcAccess->AllSharedComponentReferencesAreFromChunks(srcAccess->EntityComponentStore)) @@ -4809,7 +4837,7 @@ internal void MoveEntitiesFromInternalAll(EntityManager srcEntities, NativeArray static void RemapChunksForFilteredMove(ref NativeArray chunks, ref NativeArray remapChunks, ref NativeArray entityRemapping, - ref NativeList managedChunks, out int managedComponentCount, + ref NativeList managedChunks, out int managedComponentCount, EntityComponentStore* dstEntityComponentStore, EntityComponentStore* srcEntityComponentStore) { int chunkCount = chunks.Length; @@ -4822,7 +4850,7 @@ static void RemapChunksForFilteredMove(ref NativeArray chunks, for (int i = 0; i < chunkCount; ++i) { var chunk = chunks[i].m_Chunk; - var archetype = chunk->Archetype; + var archetype = srcEntityComponentStore->GetArchetype(chunk); // Move Chunk World. ChangeVersion:Yes OrderVersion:Yes if (previousSrcArchetype != archetype) @@ -4835,13 +4863,13 @@ static void RemapChunksForFilteredMove(ref NativeArray chunks, if (dstArchetype->NumManagedComponents > 0) { - managedComponentCount += chunk->Count * dstArchetype->NumManagedComponents; - managedChunks.Add((IntPtr)chunk); + managedComponentCount += chunk.Count * dstArchetype->NumManagedComponents; + managedChunks.Add(chunk); } if (archetype->MetaChunkArchetype != null) { - Entity srcEntity = chunk->metaChunkEntity; + Entity srcEntity = chunk.MetaChunkEntity; Entity dstEntity; dstEntityComponentStore->CreateEntities(dstArchetype->MetaChunkArchetype, &dstEntity, 1); @@ -4849,8 +4877,11 @@ static void RemapChunksForFilteredMove(ref NativeArray chunks, var srcEntityInChunk = srcEntityComponentStore->GetEntityInChunk(srcEntity); var dstEntityInChunk = dstEntityComponentStore->GetEntityInChunk(dstEntity); - ChunkDataUtility.CopyComponents(srcEntityInChunk.Chunk, srcEntityInChunk.IndexInChunk, dstEntityInChunk.Chunk, dstEntityInChunk.IndexInChunk, 1, - dstEntityComponentStore->GlobalSystemVersion); + var srcMetaArchetype = srcEntityComponentStore->GetArchetype(srcEntity); + var dstMetaArchetype = dstEntityComponentStore->GetArchetype(dstEntity); + + ChunkDataUtility.CopyComponents(srcEntityInChunk.Chunk, srcMetaArchetype, srcEntityInChunk.IndexInChunk, + dstEntityInChunk.Chunk, dstMetaArchetype, dstEntityInChunk.IndexInChunk, 1, dstEntityComponentStore->GlobalSystemVersion); EntityRemapUtility.AddEntityRemapping(ref entityRemapping, srcEntity, dstEntity); toDestroy.Add(srcEntity); @@ -4881,7 +4912,7 @@ internal void MoveChunksFromFiltered( int managedComponentCount = 0; var remapChunks = new NativeArray(chunks.Length, Allocator.TempJob); - NativeList managedChunks = new NativeList(0, Allocator.TempJob); + var managedChunks = new NativeList(0, Allocator.TempJob); RemapChunksForFilteredMove(ref chunks, ref remapChunks, ref entityRemapping, ref managedChunks, out managedComponentCount, ecs, srcEntityComponentStore); NativeArray srcManagedIndices = default; @@ -4891,8 +4922,7 @@ internal void MoveChunksFromFiltered( { srcManagedIndices = new NativeArray(managedComponentCount, Allocator.TempJob); dstManagedIndices = new NativeArray(managedComponentCount, Allocator.TempJob); - new - GatherManagedComponentIndicesInChunkJob() + new GatherManagedComponentIndicesInChunkJob { SrcEntityComponentStore = srcEntityComponentStore, DstEntityComponentStore = ecs, @@ -4937,6 +4967,7 @@ internal void MoveChunksFromFiltered( { RemapChunks = remapChunks, RemapShared = remapShared, + SrcEntityComponentStore = srcEntityComponentStore, }.Schedule(remapChunksJob); moveChunksBetweenArchetypeJob.Complete(); @@ -5091,7 +5122,7 @@ internal void MoveChunksFromAll( internal struct RemapChunk { - public Chunk* chunk; + public ChunkIndex chunk; public Archetype* dstArchetype; } @@ -5114,9 +5145,9 @@ public void Execute() for (int i = 0; i < RemapChunks.Length; i++) { var remapChunk = RemapChunks[i]; - Chunk* chunk = remapChunk.chunk; + var chunk = remapChunk.chunk; Archetype* dstArchetype = remapChunk.dstArchetype; - EntityComponentStore->ManagedChangesTracker.PatchEntities(dstArchetype, chunk, chunk->Count, EntityRemapping); + EntityComponentStore->ManagedChangesTracker.PatchEntities(dstArchetype, chunk, chunk.Count, EntityRemapping); } } } @@ -5164,8 +5195,8 @@ public void Execute() for (int i = 0; i < numManagedComponents; ++i) { int type = i + firstManagedComponent; - var a = (int*)ChunkDataUtility.GetComponentDataRO(chunk, 0, type); - for (int ei = 0; ei < chunk->Count; ++ei) + var a = (int*)ChunkDataUtility.GetComponentDataRO(chunk, srcArchetype, 0, type); + for (int ei = 0, count = chunk.Count; ei < count; ++ei) { var managedComponentIndex = a[ei]; if (managedComponentIndex == 0) @@ -5187,7 +5218,7 @@ struct GatherManagedComponentIndicesInChunkJob : IJob { [NativeDisableUnsafePtrRestriction] public EntityComponentStore* SrcEntityComponentStore; [NativeDisableUnsafePtrRestriction] public EntityComponentStore* DstEntityComponentStore; - public NativeArray Chunks; + public NativeArray Chunks; public NativeArray SrcManagedIndices; public NativeArray DstManagedIndices; @@ -5202,15 +5233,15 @@ public void Execute() int srcCounter = 0; for (var iChunk = 0; iChunk < Chunks.Length; ++iChunk) { - var chunk = (Chunk*)Chunks[iChunk]; - var srcArchetype = chunk->Archetype; + var chunk = Chunks[iChunk]; + var srcArchetype = SrcEntityComponentStore->GetArchetype(chunk); var firstManagedComponent = srcArchetype->FirstManagedComponent; var numManagedComponents = srcArchetype->NumManagedComponents; for (int i = 0; i < numManagedComponents; ++i) { int type = i + firstManagedComponent; - var a = (int*)ChunkDataUtility.GetComponentDataRO(chunk, 0, type); - for (int ei = 0; ei < chunk->Count; ++ei) + var a = (int*)ChunkDataUtility.GetComponentDataRO(chunk, srcArchetype, 0, type); + for (int ei = 0, entityCount = chunk.Count; ei < entityCount; ++ei) { var managedComponentIndex = a[ei]; if (managedComponentIndex == 0) @@ -5240,13 +5271,14 @@ struct RemapChunksFilteredJob : IJobParallelFor public void Execute(int index) { - Chunk* chunk = remapChunks[index].chunk; + var chunk = remapChunks[index].chunk; Archetype* dstArchetype = remapChunks[index].dstArchetype; - dstEntityComponentStore->RemapChunk(dstArchetype, chunk, 0, chunk->Count, ref entityRemapping); + var entityCount = chunk.Count; + dstEntityComponentStore->RemapChunk(chunk, 0, entityCount, ref entityRemapping); EntityRemapUtility.PatchEntities(dstArchetype->ScalarEntityPatches + 1, dstArchetype->ScalarEntityPatchCount - 1, dstArchetype->BufferEntityPatches, - dstArchetype->BufferEntityPatchCount, chunk->Buffer, chunk->Count, ref entityRemapping); + dstArchetype->BufferEntityPatchCount, chunk.Buffer, entityCount, ref entityRemapping); // Fix up chunk pointers in ChunkHeaders if (dstArchetype->HasChunkComponents) @@ -5258,9 +5290,9 @@ public void Execute(int index) // Set chunk header without bumping change versions since they are zeroed when processing meta chunk // modifying them here would be a race condition - var metaChunkEntity = chunk->metaChunkEntity; + var metaChunkEntity = chunk.MetaChunkEntity; var metaEntityInChunk = dstEntityComponentStore->GetEntityInChunk(metaChunkEntity); - var chunkHeader = (ChunkHeader*)(metaEntityInChunk.Chunk->Buffer + (offset + sizeOf * metaEntityInChunk.IndexInChunk)); + var chunkHeader = (ChunkHeader*)(metaEntityInChunk.Chunk.Buffer + (offset + sizeOf * metaEntityInChunk.IndexInChunk)); chunkHeader->ArchetypeChunk = new ArchetypeChunk(chunk, dstEntityComponentStore); } } @@ -5271,6 +5303,7 @@ struct MoveFilteredChunksBetweenArchetypeJob : IJob { [Collections.ReadOnly] public NativeArray RemapChunks; [Collections.ReadOnly] public NativeHashMap RemapShared; + [NativeDisableUnsafePtrRestriction] public EntityComponentStore* SrcEntityComponentStore; public void Execute() { @@ -5280,9 +5313,10 @@ public void Execute() for (int iChunk = 0; iChunk < chunkCount; ++iChunk) { var chunk = RemapChunks[iChunk].chunk; + var srcArchetype = SrcEntityComponentStore->GetArchetype(chunk); var dstArchetype = RemapChunks[iChunk].dstArchetype; int numSharedComponents = dstArchetype->NumSharedComponents; - var sharedComponentValues = chunk->SharedComponentValues; + var sharedComponentValues = srcArchetype->Chunks.GetSharedComponentValues(chunk.ListIndex); if (numSharedComponents != 0) { for (int i = 0; i < numSharedComponents; ++i) @@ -5298,7 +5332,7 @@ public void Execute() sharedComponentValues = sharedComponentCopy; } - ChunkDataUtility.MoveArchetype(chunk, dstArchetype, sharedComponentValues); + ChunkDataUtility.MoveArchetype(srcArchetype, chunk, dstArchetype, sharedComponentValues); } } } @@ -5313,17 +5347,18 @@ struct RemapAllChunksJob : IJobParallelFor public void Execute(int index) { - Chunk* chunk = remapChunks[index].chunk; + var chunk = remapChunks[index].chunk; Archetype* dstArchetype = remapChunks[index].dstArchetype; - dstEntityComponentStore->RemapChunk(dstArchetype, chunk, 0, chunk->Count, ref entityRemapping); + var entityCount = chunk.Count; + dstEntityComponentStore->RemapChunk(chunk, 0, entityCount, ref entityRemapping); EntityRemapUtility.PatchEntities(dstArchetype->ScalarEntityPatches + 1, dstArchetype->ScalarEntityPatchCount - 1, dstArchetype->BufferEntityPatches, - dstArchetype->BufferEntityPatchCount, chunk->Buffer, chunk->Count, ref entityRemapping); + dstArchetype->BufferEntityPatchCount, chunk.Buffer, entityCount, ref entityRemapping); - chunk->Archetype = dstArchetype; - chunk->ListIndex += dstArchetype->Chunks.Count; - chunk->ListWithEmptySlotsIndex += dstArchetype->ChunksWithEmptySlots.Length; + dstEntityComponentStore->SetArchetype(chunk, dstArchetype); + chunk.ListIndex += dstArchetype->Chunks.Count; + chunk.ListWithEmptySlotsIndex += dstArchetype->ChunksWithEmptySlots.Length; } } @@ -5383,14 +5418,15 @@ public void Execute(int index) } } + var dstCapacity = dstArchetype->ChunkCapacity; for (int i = 0; i < srcChunkCount; ++i) { var chunk = dstArchetype->Chunks[i + dstChunkCount]; - if (chunk->Count < chunk->Capacity) - dstArchetype->FreeChunksBySharedComponents.Add(dstArchetype->Chunks[i + dstChunkCount]); + if (chunk.Count < dstCapacity) + dstArchetype->FreeChunksBySharedComponents.Add(chunk); } - srcArchetype->FreeChunksBySharedComponents.Init(16); + srcArchetype->FreeChunksBySharedComponents.Init(srcArchetype, 16); } var globalSystemVersion = dstEntityComponentStore->GlobalSystemVersion; @@ -5420,7 +5456,7 @@ public void Execute(int index) { int dstChunkIndex = dstChunkCount + srcChunkIndex; dstArchetype->Chunks.InitializeDisabledCountForChunk(dstChunkIndex); - var chunkEntityCount = dstArchetype->Chunks[dstChunkIndex]->Count; + var chunkEntityCount = dstArchetype->Chunks[dstChunkIndex].Count; for (int t = 0; t < dstArchetype->EnableableTypesCount; ++t) { var dstIndexInArchetype = dstArchetype->EnableableTypeIndexInArchetype[t]; @@ -5445,9 +5481,9 @@ public void Execute(int index) // Set chunk header without bumping change versions since they are zeroed when processing meta chunk // modifying them here would be a race condition var chunk = dstArchetype->Chunks[i + dstChunkCount]; - var metaChunkEntity = chunk->metaChunkEntity; + var metaChunkEntity = chunk.MetaChunkEntity; var metaEntityInChunk = dstEntityComponentStore->GetEntityInChunk(metaChunkEntity); - var chunkHeader = (ChunkHeader*)(metaEntityInChunk.Chunk->Buffer + (offset + sizeOf * metaEntityInChunk.IndexInChunk)); + var chunkHeader = (ChunkHeader*)(metaEntityInChunk.Chunk.Buffer + (offset + sizeOf * metaEntityInChunk.IndexInChunk)); chunkHeader->ArchetypeChunk = new ArchetypeChunk(chunk, dstEntityComponentStore); } } @@ -5493,12 +5529,15 @@ internal uint GetChunkVersionHash(Entity entity) return 0; var chunk = ecs->GetChunk(entity); - var typeCount = chunk->Archetype->TypesCount; + var archetype = ecs->GetArchetype(chunk); + var typeCount = archetype->TypesCount; uint hash = 0; + var archetypeChunkData = archetype->Chunks; + var chunkListIndex = chunk.ListIndex; for (int i = 0; i < typeCount; ++i) { - hash += chunk->GetChangeVersion(i); + hash += archetypeChunkData.GetChangeVersion(i, chunkListIndex); } return hash; @@ -5707,7 +5746,7 @@ public static unsafe partial class EntityManagerManagedComponentExtensions $"GetChunkComponentData<{typeName}> can not be called with an invalid archetype chunk."); } #endif - var metaChunkEntity = chunk.m_Chunk->metaChunkEntity; + var metaChunkEntity = chunk.m_Chunk.MetaChunkEntity; return manager.GetComponentData(metaChunkEntity); } @@ -5727,7 +5766,7 @@ public static unsafe partial class EntityManagerManagedComponentExtensions var access = manager.GetCheckedEntityDataAccess(); access->EntityComponentStore->AssertEntitiesExist(&entity, 1); var chunk = access->EntityComponentStore->GetChunk(entity); - var metaChunkEntity = chunk->metaChunkEntity; + var metaChunkEntity = chunk.MetaChunkEntity; return access->GetComponentData(metaChunkEntity, access->ManagedComponentStore); } @@ -5753,7 +5792,7 @@ public static unsafe partial class EntityManagerManagedComponentExtensions $"SetChunkComponentData<{typeName}> can not be called with an invalid archetype chunk."); } #endif - var metaChunkEntity = chunk.m_Chunk->metaChunkEntity; + var metaChunkEntity = chunk.m_Chunk.MetaChunkEntity; manager.SetComponentData(metaChunkEntity, componentValue); } @@ -5891,43 +5930,27 @@ public static unsafe partial class EntityManagerManagedComponentExtensions access->AssertQueryIsValid(entityQuery); - bool validAdd = true; - var chunks = entityQuery.ToArchetypeChunkArray(Allocator.TempJob); - + using var chunks = entityQuery.ToArchetypeChunkArray(Allocator.TempJob); if (chunks.Length > 0) { - ecs->CheckCanAddChunkComponent(chunks, ComponentType.ChunkComponent(), ref validAdd); - - if (validAdd) - { - var changes = access->BeginStructuralChanges(); - - var type = ComponentType.ReadWrite(); - var chunkType = ComponentType.FromTypeIndex(TypeManager.MakeChunkComponentTypeIndex(type.TypeIndex)); - - StructuralChange.AddComponentChunks(ecs, (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks), chunks.Length, chunkType.TypeIndex); + ecs->AssertCanAddComponent(entityQuery._GetImpl(), ComponentType.ChunkComponent()); + var type = ComponentType.ReadWrite(); + var chunkType = ComponentType.FromTypeIndex(TypeManager.MakeChunkComponentTypeIndex(type.TypeIndex)); - access->EndStructuralChanges(ref changes); - } + var changes = access->BeginStructuralChanges(); + StructuralChange.AddComponentChunks(ecs, (ArchetypeChunk*)NativeArrayUnsafeUtility.GetUnsafePtr(chunks), chunks.Length, chunkType.TypeIndex); + access->EndStructuralChanges(ref changes); 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, new() { - var type = TypeManager.GetTypeIndex(); for (int i = 0; i < chunks.Length; i++) { var srcChunk = chunks[i].m_Chunk; - manager.SetComponentData(srcChunk->metaChunkEntity, componentData); + manager.SetComponentData(srcChunk.MetaChunkEntity, componentData); } } } diff --git a/Unity.Entities/EntityManagerDebug.cs b/Unity.Entities/EntityManagerDebug.cs index f7d545c..7f62221 100644 --- a/Unity.Entities/EntityManagerDebug.cs +++ b/Unity.Entities/EntityManagerDebug.cs @@ -183,12 +183,12 @@ public EntityManagerDebug(EntityManager entityManager) /// The value to set for any unused chunk data public void PoisonUnusedDataInAllChunks(EntityArchetype archetype, byte value) { - Unity.Entities.EntityComponentStore.AssertValidArchetype(m_Manager.GetCheckedEntityDataAccess()->EntityComponentStore, archetype); + EntityComponentStore.AssertValidArchetype(m_Manager.GetCheckedEntityDataAccess()->EntityComponentStore, archetype); for (var i = 0; i < archetype.Archetype->Chunks.Count; ++i) { var chunk = archetype.Archetype->Chunks[i]; - ChunkDataUtility.MemsetUnusedChunkData(chunk, value); + ChunkDataUtility.MemsetUnusedChunkData(archetype.Archetype, chunk.Buffer, value, chunk.Count); } } @@ -211,7 +211,6 @@ public bool IsSharedComponentManagerEmpty() return m_Manager.GetCheckedEntityDataAccess()->ManagedComponentStore.IsEmpty(); } -#if !NET_DOTS internal static string GetArchetypeDebugString(Archetype* a) { var buf = new System.Text.StringBuilder(); @@ -229,8 +228,6 @@ internal static string GetArchetypeDebugString(Archetype* a) return buf.ToString(); } -#endif - /// /// The number of entities in the referenced EntityManager /// @@ -245,6 +242,33 @@ public int EntityCount } } + public int HighestIndexEntity + { + get + { + var maxIndex = -1; + + var entityDataAccess = m_Manager.GetCheckedEntityDataAccess(); + var archetypes = entityDataAccess->EntityComponentStore->m_Archetypes; + for (int ai = 0, ac = archetypes.Length; ai < ac; ai++) + { + var chunks = archetypes[ai]->Chunks; + for (int ci = 0, cc = chunks.Count; ci < cc; ci++) + { + var chunk = chunks[ci]; + var entities = (Entity*)chunk.Buffer; + for (int ei = 0, ec = chunk.Count; ei < ec; ei++) + { + var index = entities[ei].Index; + maxIndex = index > maxIndex ? index : maxIndex; + } + } + } + + return maxIndex; + } + } + /// /// Determines if chunks created in the use the specified /// @@ -265,12 +289,12 @@ public byte MemoryInitPattern internal Entity GetMetaChunkEntity(Entity entity) { - return m_Manager.GetChunk(entity).m_Chunk->metaChunkEntity; + return m_Manager.GetChunk(entity).m_Chunk.MetaChunkEntity; } internal Entity GetMetaChunkEntity(ArchetypeChunk chunk) { - return chunk.m_Chunk->metaChunkEntity; + return chunk.m_Chunk.MetaChunkEntity; } #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG @@ -339,22 +363,9 @@ public string GetEntityInfo(Entity entity) { return "Entity.Invalid"; } -#if !NET_DOTS var str = new System.Text.StringBuilder(); GetEntityInfo(entity, false, str); return str.ToString(); -#else - // @TODO Tiny really needs a proper string/stringutils implementation - var archetype = entityComponentStore->GetArchetype(entity); - string str = $"Entity {entity.Index}.{entity.Version}"; - for (var i = 0; i < archetype->TypesCount; i++) - { - var componentTypeInArchetype = archetype->Types[i]; - str += " - {0}" + componentTypeInArchetype.ToString(); - } - - return str; -#endif } /// @@ -365,11 +376,12 @@ public string GetEntityInfo(Entity entity) /// The name of the system that modified it if found public static string GetLastWriterSystemName(ArchetypeChunk chunk, ComponentType componentType) { - var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(chunk.Archetype.Archetype, componentType.TypeIndex); + var archetype = chunk.Archetype.Archetype; + var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(archetype, componentType.TypeIndex); if (typeIndexInArchetype == -1) return $"'{componentType}' was not present on the chunk."; - var changeVersion = chunk.m_Chunk->GetChangeVersion(typeIndexInArchetype); + var changeVersion = archetype->Chunks.GetChangeVersion(typeIndexInArchetype, chunk.m_Chunk.ListIndex); var system = World.FindSystemStateForChangeVersion(chunk.m_EntityComponentStore, changeVersion); if (system == null) @@ -378,7 +390,6 @@ public static string GetLastWriterSystemName(ArchetypeChunk chunk, ComponentType return system->DebugName.ToString(); } -#if !NET_DOTS internal void GetEntityInfo(Entity entity, bool includeComponentValues, System.Text.StringBuilder str) { var entityComponentStore = m_Manager.GetCheckedEntityDataAccess()->EntityComponentStore; @@ -416,7 +427,6 @@ internal void GetEntityInfo(Entity entity, bool includeComponentValues, System.T } #endif } -#endif /// /// Gets the component object of a given entity diff --git a/Unity.Entities/EntityRemapUtility.cs b/Unity.Entities/EntityRemapUtility.cs index 5215995..c9f7389 100644 --- a/Unity.Entities/EntityRemapUtility.cs +++ b/Unity.Entities/EntityRemapUtility.cs @@ -137,8 +137,6 @@ public struct BufferEntityPatchInfo public int ElementStride; } -#if !UNITY_DOTSRUNTIME - /// /// Calculates the field offsets. /// @@ -369,8 +367,6 @@ static void ProcessEntityOrBlobReferencesRecursiveManaged(Type type, ref HasRefR } -#endif - /// /// Adds elements for each of the input offsets. /// diff --git a/Unity.Entities/IJobChunk.cs b/Unity.Entities/IJobChunk.cs index e486a1a..f65e252 100644 --- a/Unity.Entities/IJobChunk.cs +++ b/Unity.Entities/IJobChunk.cs @@ -233,6 +233,8 @@ internal static unsafe void RunByRefWithoutJobs(this ref T jobData, EntityQue where T : struct, IJobChunk { var queryImpl = query._GetImpl(); + if (queryImpl->_Access->IsInExclusiveTransaction) + throw new InvalidOperationException("You can't schedule a job while an exclusive transaction is active"); #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG queryImpl->_Access->DependencyManager->ForEachStructuralChange.BeginIsInForEach(queryImpl); #endif @@ -258,13 +260,13 @@ internal static unsafe void RunByRefWithoutJobs(this ref T jobData, EntityQue { // Fast path for queries with no filtering and no enableable components var cachedChunkList = queryImpl->GetMatchingChunkCache(); - var chunkPtr = cachedChunkList.Ptr; + var chunkIndices = cachedChunkList.ChunkIndices; int chunkCount = cachedChunkList.Length; - ArchetypeChunk chunk = new ArchetypeChunk(null, cachedChunkList.EntityComponentStore); + ArchetypeChunk chunk = new ArchetypeChunk(ChunkIndex.Null, cachedChunkList.EntityComponentStore); v128 defaultMask = default; for (int chunkIndex = 0; chunkIndex < chunkCount; ++chunkIndex) { - chunk.m_Chunk = chunkPtr[chunkIndex]; + chunk.m_Chunk = chunkIndices[chunkIndex]; Assert.AreNotEqual(0, chunk.Count); jobData.Execute(chunk, chunkIndex, false, defaultMask); } @@ -283,6 +285,8 @@ internal static unsafe JobHandle ScheduleInternal( where T : struct, IJobChunk { var queryImpl = query._GetImpl(); + if (queryImpl->_Access->IsInExclusiveTransaction) + throw new InvalidOperationException("You can't schedule a job while an exclusive transaction is active"); var queryData = queryImpl->_QueryData; var cachedChunks = queryImpl->GetMatchingChunkCache(); var totalChunkCount = cachedChunks.Length; @@ -383,31 +387,35 @@ internal unsafe static void ExecuteInternal( // If we have no range to steal, exit the loop. if (!JobsUtility.GetWorkStealingRange(ref ranges, jobIndex, out beginChunkIndex, out endChunkIndex)) break; - -#if ENABLE_UNITY_COLLECTIONS_CHECKS - // By default, set a zero-sized range of valid array indices - JobsUtility.PatchBufferMinMaxRanges(bufferRangePatchData, UnsafeUtility.AddressOf(ref jobWrapper), 0, 0); -#endif } // Do the actual user work. if (jobWrapper.QueryHasEnableableComponents == 0 && !isFiltering) { // Fast path with no entity/chunk filtering active: we can just iterate over the cached chunk list directly. - var chunkPtr = chunks.Ptr; - ArchetypeChunk chunk = new ArchetypeChunk(null, chunks.EntityComponentStore); + var chunkIndices = chunks.ChunkIndices; + ArchetypeChunk chunk = new ArchetypeChunk(ChunkIndex.Null, chunks.EntityComponentStore); v128 defaultMask = default; for (int chunkIndex = beginChunkIndex; chunkIndex < endChunkIndex; ++chunkIndex) { - chunk.m_Chunk = chunkPtr[chunkIndex]; + chunk.m_Chunk = chunkIndices[chunkIndex]; Assert.AreNotEqual(0, chunk.Count); #if ENABLE_UNITY_COLLECTIONS_CHECKS + // For containers passed in the job struct, parallel jobs can limit writes to a range of indices. + // By default, writes are limited to the element corresponding to the unfiltered chunk index. + // If the user has provided a ChunkBaseEntityIndices array, use that instead (limiting writes to the range of entities within the chunk). + // This range check can be disabled on a per-container basis by adding [NativeDisableParallelForRestriction] to the container field in the job struct. if (Hint.Unlikely(jobWrapper.ChunkBaseEntityIndices != null)) { int chunkBaseEntityIndex = jobWrapper.ChunkBaseEntityIndices[chunkIndex]; JobsUtility.PatchBufferMinMaxRanges(bufferRangePatchData, UnsafeUtility.AddressOf(ref jobWrapper), chunkBaseEntityIndex, chunk.Count); } + else if (isParallel) + { + JobsUtility.PatchBufferMinMaxRanges(bufferRangePatchData, + UnsafeUtility.AddressOf(ref jobWrapper), chunkIndex, 1); + } #endif jobWrapper.JobData.Execute(chunk, chunkIndex, false, defaultMask); } @@ -424,12 +432,21 @@ internal unsafe static void ExecuteInternal( out byte useEnabledMask, ref chunkEnabledMask)) { #if ENABLE_UNITY_COLLECTIONS_CHECKS + // For containers passed in the job struct, parallel jobs can limit writes to a range of indices. + // By default, writes are limited to the element corresponding to the unfiltered chunk index. + // If the user has provided a ChunkBaseEntityIndices array, use that instead (limiting writes to the range of entities within the chunk). + // This range check can be disabled on a per-container basis by adding [NativeDisableParallelForRestriction] to the container field in the job struct. if (Hint.Unlikely(jobWrapper.ChunkBaseEntityIndices != null)) { int chunkBaseEntityIndex = jobWrapper.ChunkBaseEntityIndices[chunkIndex]; JobsUtility.PatchBufferMinMaxRanges(bufferRangePatchData, UnsafeUtility.AddressOf(ref jobWrapper), chunkBaseEntityIndex, chunk.Count); } + else if (isParallel) + { + JobsUtility.PatchBufferMinMaxRanges(bufferRangePatchData, + UnsafeUtility.AddressOf(ref jobWrapper), chunkIndex, 1); + } #endif jobWrapper.JobData.Execute(chunk, chunkIndex, useEnabledMask != 0, chunkEnabledMask); diff --git a/Unity.Entities/IJobEntity.cs b/Unity.Entities/IJobEntity.cs index 23595ea..4e7465d 100644 --- a/Unity.Entities/IJobEntity.cs +++ b/Unity.Entities/IJobEntity.cs @@ -140,6 +140,21 @@ public sealed class WithDisabledAttribute : Attribute public WithDisabledAttribute(params Type[] types){} } + /// + /// Specify that an IJobEntity should include all of the component types specified as part of the attribute, whether or not the components + /// are enabled on individual entities. + /// + [AttributeUsage(AttributeTargets.Struct, AllowMultiple = true)] + public sealed class WithPresentAttribute : Attribute + { + /// + /// Specifies that this IJobEntity should include all of the component types specified as part of the attribute, whether or not the components + /// are enabled on individual entities. + /// + /// Present component types + public WithPresentAttribute(params Type[] types){} + } + /// /// Specifies that this IJobEntity should include all ComponentTypes found as attributes of the IJobEntity /// diff --git a/Unity.Entities/Internal/InternalCompilerInterface.cs b/Unity.Entities/Internal/InternalCompilerInterface.cs index 3061a5c..7219a7a 100644 --- a/Unity.Entities/Internal/InternalCompilerInterface.cs +++ b/Unity.Entities/Internal/InternalCompilerInterface.cs @@ -2,9 +2,9 @@ using System.Runtime.CompilerServices; using Unity.Burst; using Unity.Burst.CompilerServices; +using Unity.Burst.Intrinsics; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -using Unity.Entities.LowLevel.Unsafe; using Unity.Jobs; using Unity.Jobs.LowLevel.Unsafe; @@ -18,6 +18,216 @@ namespace Unity.Entities.Internal [System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)] public static partial class InternalCompilerInterface { + public interface IAspectLookup where T : IAspect + { + public void Update(ref SystemState state); + public T this[Entity entity] { get; } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EntityStorageInfoLookup GetEntityStorageInfoLookup( + ref EntityStorageInfoLookup entityStorageInfoLookup, ref SystemState state) + { + entityStorageInfoLookup.Update(ref state); + return entityStorageInfoLookup; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool DoesEntityExist(ref EntityStorageInfoLookup entityStorageInfoLookup, ref SystemState state, + Entity entity) + { + entityStorageInfoLookup.Update(ref state); + return entityStorageInfoLookup.Exists(entity); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static EntityTypeHandle GetEntityTypeHandle(ref EntityTypeHandle entityTypeHandle, ref SystemState state) + { + entityTypeHandle.Update(ref state); + return entityTypeHandle; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ComponentLookup GetComponentLookup(ref ComponentLookup componentLookup, + ref SystemState state) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + return componentLookup; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BufferLookup GetBufferLookup(ref BufferLookup bufferLookup, ref SystemState state) + where T : unmanaged, IBufferElementData + { + bufferLookup.Update(ref state); + return bufferLookup; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RefRO GetComponentROAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + Entity entity) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRO(); + return componentLookup.GetRefRO(entity); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RefRW GetComponentRWAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + Entity entity) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRW(); + return componentLookup.GetRefRW(entity); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T GetComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, Entity entity) + where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRO(); + return componentLookup[entity]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static RefRW GetComponentRWAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + SystemHandle systemHandle) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRW(); + return componentLookup.GetRefRW(systemHandle); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T GetComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + SystemHandle systemHandle) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRO(); + return componentLookup[systemHandle]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, T component, + Entity entity) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRW(); + componentLookup[entity] = component; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, T component, + SystemHandle systemHandle) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRW(); + componentLookup[systemHandle] = component; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool HasComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, Entity entity) + where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRO(); + return componentLookup.HasComponent(entity); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool HasComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + SystemHandle systemHandle) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRO(); + return componentLookup.HasComponent(systemHandle); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsComponentEnabledAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + Entity entity) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRO(); + return componentLookup.IsComponentEnabled(entity); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsComponentEnabledAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + SystemHandle systemHandle) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRO(); + return componentLookup.IsComponentEnabled(systemHandle); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetComponentEnabledAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + Entity entity, bool value) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRW(); + componentLookup.SetComponentEnabled(entity, value); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetComponentEnabledAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + SystemHandle systemHandle, bool value) where T : unmanaged, IComponentData + { + componentLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRW(); + componentLookup.SetComponentEnabled(systemHandle, value); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static DynamicBuffer GetBufferAfterCompletingDependency(ref BufferLookup bufferLookup, ref SystemState state, + Entity entity) where T : unmanaged, IBufferElementData + { + bufferLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRW(); + return bufferLookup[entity]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool HasBufferAfterCompletingDependency(ref BufferLookup bufferLookup, ref SystemState state, Entity entity) + where T : unmanaged, IBufferElementData + { + bufferLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRO(); + return bufferLookup.HasBuffer(entity); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool IsBufferEnabledAfterCompletingDependency(ref BufferLookup bufferLookup, ref SystemState state, Entity entity) + where T : unmanaged, IBufferElementData + { + bufferLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRO(); + return bufferLookup.IsBufferEnabled(entity); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void SetBufferEnabledAfterCompletingDependency(ref BufferLookup bufferLookup, ref SystemState state, Entity entity, bool value) where T : unmanaged, IBufferElementData + { + bufferLookup.Update(ref state); + state.EntityManager.CompleteDependencyBeforeRW(); + bufferLookup.SetBufferEnabled(entity, value); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static T GetAspectAfterCompletingDependency(ref TLookup aspectLookup, ref SystemState state, bool isAspectReadOnly, Entity entity) + where TLookup : struct, IAspectLookup + where T : struct, IAspect, IAspectCreate + { + aspectLookup.Update(ref state); + if (isAspectReadOnly) + default(T).CompleteDependencyBeforeRO(ref state); + else + default(T).CompleteDependencyBeforeRW(ref state); + return aspectLookup[entity]; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static ComponentTypeHandle GetComponentTypeHandle(ref ComponentTypeHandle componentTypeHandle, + ref SystemState state) where T : unmanaged, IComponentData + { + componentTypeHandle.Update(ref state); + return componentTypeHandle; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static BufferTypeHandle GetBufferTypeHandle(ref BufferTypeHandle bufferTypeHandle, + ref SystemState state) where T : unmanaged, IBufferElementData + { + bufferTypeHandle.Update(ref state); + return bufferTypeHandle; + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static SharedComponentTypeHandle GetSharedComponentTypeHandle( + ref SharedComponentTypeHandle sharedComponentTypeHandle, ref SystemState state) + where T : struct, ISharedComponentData + { + sharedComponentTypeHandle.Update(ref state); + return sharedComponentTypeHandle; + } + public static JobRunWithoutJobSystemDelegate BurstCompile(JobRunWithoutJobSystemDelegate d) => BurstCompiler.CompileFunctionPointer(d).Invoke; public static JobChunkRunWithoutJobSystemDelegate BurstCompile(JobChunkRunWithoutJobSystemDelegate d) => BurstCompiler.CompileFunctionPointer(d).Invoke; public static JobChunkRunWithoutJobSystemDelegateLimitEntities BurstCompile(JobChunkRunWithoutJobSystemDelegateLimitEntities d) => BurstCompiler.CompileFunctionPointer(d).Invoke; @@ -71,6 +281,10 @@ public static unsafe IntPtr UnsafeGetChunkNativeArrayReadOnlyIntPtr(Archetype public static unsafe IntPtr UnsafeGetChunkNativeArrayIntPtr(ArchetypeChunk chunk, ref ComponentTypeHandle typeHandle) where T : unmanaged, IComponentData => (IntPtr) chunk.GetRequiredComponentDataPtrRW(ref typeHandle); + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static bool UnsafeTryGetNextEnabledBitRange(v128 mask, int firstIndexToCheck, out int nextRangeBegin, out int nextRangeEnd) => + EnabledBitUtility.TryGetNextRange(mask, firstIndexToCheck, out nextRangeBegin, out nextRangeEnd); + /// /// There is no need to conduct the same checks in this method as we do in `GetRequiredComponentDataPtrRO` and `GetRequiredComponentDataPtrRW` -- /// the source-generator has already ensured that everything is correctly set up. @@ -79,7 +293,7 @@ public static unsafe IntPtr UnsafeGetChunkNativeArrayIntPtr(ArchetypeChunk ch public static unsafe IntPtr UnsafeGetChunkNativeArrayReadOnlyIntPtrWithoutChecks(in ArchetypeChunk chunk, ref ComponentTypeHandle typeHandle) where T : unmanaged, IComponentData => (IntPtr)ChunkDataUtility.GetComponentDataWithTypeRO( chunk.m_Chunk, - chunk.m_Chunk->Archetype, + chunk.Archetype.Archetype, 0, typeHandle.m_TypeIndex, ref typeHandle.m_LookupCache); @@ -94,7 +308,7 @@ public static unsafe IntPtr UnsafeGetChunkNativeArrayIntPtrWithoutChecks(in A byte* ptr = ChunkDataUtility.GetComponentDataWithTypeRW( chunk.m_Chunk, - chunk.m_Chunk->Archetype, + chunk.Archetype.Archetype, 0, typeHandle.m_TypeIndex, typeHandle.GlobalSystemVersion, diff --git a/Unity.Entities/Internal/InternalCompilerInterface_RunMethods_NotRT.cs b/Unity.Entities/Internal/InternalCompilerInterface_RunMethods_NotRT.cs index a100fe1..944f8a6 100644 --- a/Unity.Entities/Internal/InternalCompilerInterface_RunMethods_NotRT.cs +++ b/Unity.Entities/Internal/InternalCompilerInterface_RunMethods_NotRT.cs @@ -1,7 +1,3 @@ -// Run methods for Non DOTS_Runtime (or IL2CPP) - -#if !(UNITY_DOTSRUNTIME && !UNITY_DOTSRUNTIME_IL2CPP) - using System; using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; @@ -34,5 +30,3 @@ public static void UnsafeRunJobChunk(ref T jobData, EntityQuery query, } } } - -#endif diff --git a/Unity.Entities/Internal/InternalCompilerInterface_RunMethods_RT.cs b/Unity.Entities/Internal/InternalCompilerInterface_RunMethods_RT.cs deleted file mode 100644 index 76a23ba..0000000 --- a/Unity.Entities/Internal/InternalCompilerInterface_RunMethods_RT.cs +++ /dev/null @@ -1,390 +0,0 @@ -// Run methods for DOTS_Runtime (and not IL2CPP) - -// DOTS Runtime always compiles against the .Net Framework which will re-order structs if they contain non-blittable data (unlike mono which -// will keep structs as Layout.Sequential). However, Burst will always assume a struct layout as if Layout.Sequential was used which presents -// a data layout mismatch that must be accounted for. The DOTS Runtime job system handles this problem by marshalling jobData structs already -// but in the case where we are calling RunJob/RunJobChunk we bypass the job system data marshalling by executing the bursted static function directly -// passing the jobData as a void*. So we must account for this marshalling here. Note we only need to do this when collection checks are on since -// job structs must be non-blittable for bursting however collection checks add reference types which Burst internally treats as IntPtr which -// allows collection checks enabled code to still burst compile. - -#if UNITY_DOTSRUNTIME &&!UNITY_DOTSRUNTIME_IL2CPP - -using System; -using System.Runtime.InteropServices; -using Unity.Burst; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Entities.CodeGeneratedJobForEach; -using Unity.Jobs; -using Unity.Jobs.LowLevel.Unsafe; - -namespace Unity.Entities.Internal -{ - /// - /// This exists only for internal use and is intended to be only used by source-generated code. - /// DO NOT USE in user code (this API will change). - /// - public static partial class InternalCompilerInterface - { - [BurstCompile] - struct JobMarshalFnLookup where T : struct, IJobBase - { - static IntPtr MarshalToBurstFn; - static IntPtr MarshalFromBurstFn; - - public static IntPtr GetMarshalToBurstFn() - { - if (MarshalToBurstFn == IntPtr.Zero) - { - var job = default(T); - var fn = job.GetMarshalToBurstMethod_Gen(); - // Pin the delegate so it can be used for the lifetime of the app. Never de-alloc - var handle = GCHandle.Alloc(fn); - MarshalToBurstFn = Marshal.GetFunctionPointerForDelegate(fn); - } - - return MarshalToBurstFn; - } - - public static IntPtr GetMarshalFromBurstFn() - { - if (MarshalFromBurstFn == IntPtr.Zero) - { - var job = default(T); - var fn = job.GetMarshalFromBurstMethod_Gen(); - // Pin the delegate so it can be used for the lifetime of the app. Never de-alloc - var handle = GCHandle.Alloc(fn); - MarshalFromBurstFn = Marshal.GetFunctionPointerForDelegate(fn); - } - - return MarshalFromBurstFn; - } - } - - [BurstCompile] // This function must be burst compiled so the offset comes from a native type layout - internal static unsafe long GetJobChunkJobDataOffset() - { - // This is a fake job just so we can get the offset. Technically dangerous since the offset would not work if we had - // more than one 'T' in the wrapper, but the job system likely can't support such a thing so passing an int here is fine - JobChunkExtensions.JobChunkWrapper wrapper = default; - return (byte*) UnsafeUtility.AddressOf(ref wrapper.JobData) - (byte*) UnsafeUtility.AddressOf(ref wrapper); - } - - public static void RunIJob(ref T jobData, JobRunWithoutJobSystemDelegate functionPointer) where T : struct, IJob, IJobBase - { - // Keep the API 'safe' so that only Entities.dll needs to be compiled with /unsafe - unsafe - { - var managedJobDataPtr = UnsafeUtility.AddressOf(ref jobData); - var unmanagedSize = jobData.GetUnmanagedJobSize_Gen(); - if (unmanagedSize != -1) - { - const int kAlignment = 16; - int alignedSize = (unmanagedSize + kAlignment - 1) & ~(kAlignment - 1); - byte* unmanagedJobData = stackalloc byte[alignedSize]; - - // The following is needed if IJob's Producer were to contain any other fields along with a T JobData. - // Keeping this code in case IJob ever changes to be like that so we know how to fix what would otherwise - // be a difficult to diagnose crash. - - //byte* alignedUnmanagedJobData = (byte*) ((UInt64) (unmanagedJobData + kAlignment - 1) & ~(UInt64) (kAlignment - 1)); - // - //// DOTS Runtime job marshalling code assumes the job is wrapped so create the wrapper and assign the jobData - // - //IJobExtensions.JobProducer jobStructData = default; - //jobStructData.JobData = jobData; - //byte* jobStructDataPtr = (byte*) UnsafeUtility.AddressOf(ref jobStructData); - // - //byte* dst = alignedUnmanagedJobData; - //byte* src = jobStructDataPtr; - //var marshalToBurstFnPtr = JobMarshalFnLookup.GetMarshalToBurstFn(); - // - //UnsafeUtility.CallFunctionPtr_pp(marshalToBurstFnPtr.ToPointer(), dst, src); - // - //// In the case of JobStruct we know the jobwrapper doesn't add - //// anything to the jobData so just pass it along, no offset required unlike JobChunk - //functionPointer((IntPtr) alignedUnmanagedJobData); - // - //// Since Run can capture locals for write back, we must write back the marshalled jobData after the job executes - //var marshalFromBurstFnPtr = JobMarshalFnLookup.GetMarshalFromBurstFn(); - //UnsafeUtility.CallFunctionPtr_pp(marshalFromBurstFnPtr.ToPointer(), src, dst); - // - //jobData = jobStructData.JobData; - - byte* dstAlignedUnmanagedJobData = (byte*) ((UInt64) (unmanagedJobData + kAlignment - 1) & ~(UInt64) (kAlignment - 1)); - byte* srcJobData = (byte*) UnsafeUtility.AddressOf(ref jobData); - - var marshalToBurstFnPtr = JobMarshalFnLookup.GetMarshalToBurstFn(); - - UnsafeUtility.CallFunctionPtr_pp(marshalToBurstFnPtr.ToPointer(), dstAlignedUnmanagedJobData, srcJobData); - - functionPointer((IntPtr) dstAlignedUnmanagedJobData); - - // Since Run can capture locals for write back, we must write back the marshalled jobData after the job executes - var marshalFromBurstFnPtr = JobMarshalFnLookup.GetMarshalFromBurstFn(); - UnsafeUtility.CallFunctionPtr_pp(marshalFromBurstFnPtr.ToPointer(), srcJobData, dstAlignedUnmanagedJobData); - } - else - { - functionPointer((IntPtr) managedJobDataPtr); - } - } - } - - public static void RunIJob(ref T jobData, FunctionPointer functionPointer) where T : struct, IJob, IJobBase - { - // Keep the API 'safe' so that only Entities.dll needs to be compiled with /unsafe - unsafe - { - var managedJobDataPtr = UnsafeUtility.AddressOf(ref jobData); - var unmanagedSize = jobData.GetUnmanagedJobSize_Gen(); - if (unmanagedSize != -1) - { - const int kAlignment = 16; - int alignedSize = (unmanagedSize + kAlignment - 1) & ~(kAlignment - 1); - byte* unmanagedJobData = stackalloc byte[alignedSize]; - - // The following is needed if IJob's Producer were to contain any other fields along with a T JobData. - // Keeping this code in case IJob ever changes to be like that so we know how to fix what would otherwise - // be a difficult to diagnose crash. - - //byte* alignedUnmanagedJobData = (byte*) ((UInt64) (unmanagedJobData + kAlignment - 1) & ~(UInt64) (kAlignment - 1)); - // - //// DOTS Runtime job marshalling code assumes the job is wrapped so create the wrapper and assign the jobData - // - //IJobExtensions.JobProducer jobStructData = default; - //jobStructData.JobData = jobData; - //byte* jobStructDataPtr = (byte*) UnsafeUtility.AddressOf(ref jobStructData); - // - //byte* dst = alignedUnmanagedJobData; - //byte* src = jobStructDataPtr; - //var marshalToBurstFnPtr = JobMarshalFnLookup.GetMarshalToBurstFn(); - // - //UnsafeUtility.CallFunctionPtr_pp(marshalToBurstFnPtr.ToPointer(), dst, src); - // - //// In the case of JobStruct we know the jobwrapper doesn't add - //// anything to the jobData so just pass it along, no offset required unlike JobChunk - //functionPointer((IntPtr) alignedUnmanagedJobData); - // - //// Since Run can capture locals for write back, we must write back the marshalled jobData after the job executes - //var marshalFromBurstFnPtr = JobMarshalFnLookup.GetMarshalFromBurstFn(); - //UnsafeUtility.CallFunctionPtr_pp(marshalFromBurstFnPtr.ToPointer(), src, dst); - // - //jobData = jobStructData.JobData; - - byte* dstAlignedUnmanagedJobData = (byte*) ((UInt64) (unmanagedJobData + kAlignment - 1) & ~(UInt64) (kAlignment - 1)); - byte* srcJobData = (byte*) UnsafeUtility.AddressOf(ref jobData); - - var marshalToBurstFnPtr = JobMarshalFnLookup.GetMarshalToBurstFn(); - - UnsafeUtility.CallFunctionPtr_pp(marshalToBurstFnPtr.ToPointer(), dstAlignedUnmanagedJobData, srcJobData); - - functionPointer.Invoke((IntPtr) dstAlignedUnmanagedJobData); - - // Since Run can capture locals for write back, we must write back the marshalled jobData after the job executes - var marshalFromBurstFnPtr = JobMarshalFnLookup.GetMarshalFromBurstFn(); - UnsafeUtility.CallFunctionPtr_pp(marshalFromBurstFnPtr.ToPointer(), srcJobData, dstAlignedUnmanagedJobData); } - else - { - functionPointer.Invoke((IntPtr) managedJobDataPtr); - } - } - } - - static unsafe void UnsafeRunJobChunkImpl(void* jobPtr, EntityQuery query, JobChunkRunWithoutJobSystemDelegate functionPointer) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - var impl = query._GetImpl(); - impl->_Access->DependencyManager->ForEachStructuralChange.BeginIsInForEach(impl); - functionPointer(ref query, (IntPtr)jobPtr); - impl->_Access->DependencyManager->ForEachStructuralChange.EndIsInForEach(); -#else - functionPointer(ref query, (IntPtr)jobPtr); -#endif - } - - static unsafe void UnsafeRunJobChunkImpl(void* jobPtr, EntityQuery query, FunctionPointer functionPointer) - { -#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - var impl = query._GetImpl(); - impl->_Access->DependencyManager->ForEachStructuralChange.BeginIsInForEach(impl); - functionPointer.Invoke(ref query, new IntPtr(jobPtr)); - impl->_Access->DependencyManager->ForEachStructuralChange.EndIsInForEach(); -#else - functionPointer.Invoke(ref query, (IntPtr)jobPtr); -#endif - } - - public static void UnsafeRunJobChunk(ref T jobData, EntityQuery query, JobChunkRunWithoutJobSystemDelegate functionPointer) - where T : struct, IJobChunk, IJobBase - { - // Keep the API 'safe' so that only Entities.dll needs to be compiled with /unsafe - unsafe - { - var managedJobDataPtr = UnsafeUtility.AddressOf(ref jobData); - var unmanagedSize = jobData.GetUnmanagedJobSize_Gen(); - if (unmanagedSize != -1) - { - const int kAlignment = 16; - int alignedSize = (unmanagedSize + kAlignment - 1) & ~(kAlignment - 1); - byte* unmanagedJobData = stackalloc byte[alignedSize]; - byte* alignedUnmanagedJobData = (byte*) ((UInt64) (unmanagedJobData + kAlignment - 1) & ~(UInt64) (kAlignment - 1)); - - // DOTS Runtime job marshalling code assumes the job is wrapped so create the wrapper and assign the jobData - - JobChunkExtensions.JobChunkWrapper jobWrapper = default; - jobWrapper.JobData = jobData; - byte* jobStructDataPtr = (byte*) UnsafeUtility.AddressOf(ref jobWrapper); - - byte* dst = alignedUnmanagedJobData; - byte* src = jobStructDataPtr; - var marshalToBurstFnPtr = JobMarshalFnLookup.GetMarshalToBurstFn(); - - UnsafeUtility.CallFunctionPtr_pp(marshalToBurstFnPtr.ToPointer(), dst, src); - - // Since we are running inline, normally the outer job scheduling code would - // reference jobWrapper.Data however we can't do that since if we are in this code it means - // we are dealing with a job/jobwrapper that is burst compiled and is non-blittable. Thus we need to get - // the JobData ptr from what Burst sees which is what we do here. - var jobDataPtr = GetJobChunkJobDataOffset() + alignedUnmanagedJobData; - UnsafeRunJobChunkImpl(jobDataPtr, query, functionPointer); - - // Since Run can capture locals for write back, we must write back the marshalled jobData after the job executes - var marshalFromBurstFnPtr = JobMarshalFnLookup.GetMarshalFromBurstFn(); - UnsafeUtility.CallFunctionPtr_pp(marshalFromBurstFnPtr.ToPointer(), src, dst); - - jobData = jobWrapper.JobData; - } - else - { - UnsafeRunJobChunkImpl(managedJobDataPtr, query, functionPointer); - } - } - } - - // Unsafe methods used to provide access for source-generated code. - // Do not use these methods outside of source-generated code. - // Unsafe methods to run jobs (replacing ILPP invoked methods) - public static void UnsafeRunIJob(ref T jobData, JobRunWithoutJobSystemDelegate functionPointer) - where T : struct, IJob, IJobBase - { - RunIJob(ref jobData, functionPointer); - } - - public static void UnsafeRunJobChunk(ref T jobData, EntityQuery query, IntPtr limitToEntityArrayPtr, int limitToEntityArrayLength, - JobChunkRunWithoutJobSystemDelegateLimitEntities functionPointer) - where T : struct, IJobChunk, IJobBase - { - // Keep the API 'safe' so that only Entities.dll needs to be compiled with /unsafe - unsafe - { - var managedJobDataPtr = UnsafeUtility.AddressOf(ref jobData); - var unmanagedSize = jobData.GetUnmanagedJobSize_Gen(); - if (unmanagedSize != -1) - { - const int kAlignment = 16; - int alignedSize = (unmanagedSize + kAlignment - 1) & ~(kAlignment - 1); - byte* unmanagedJobWrapper = stackalloc byte[alignedSize]; - byte* alignedUnmanagedJobWrapper = (byte*) ((UInt64) (unmanagedJobWrapper + kAlignment - 1) & ~(UInt64) (kAlignment - 1)); - - // DOTS Runtime job marshalling code assumes the job is wrapped so create the wrapper and assign the jobData - - JobChunkExtensions.JobChunkWrapper jobStructData = default; - jobStructData.JobData = jobData; - byte* jobStructDataPtr = (byte*) UnsafeUtility.AddressOf(ref jobStructData); - - byte* dst = alignedUnmanagedJobWrapper; - byte* src = jobStructDataPtr; - var marshalToBurstFnPtr = JobMarshalFnLookup.GetMarshalToBurstFn(); - -#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - var impl = query._GetImpl(); - impl->_Access->DependencyManager->ForEachStructuralChange.BeginIsInForEach(impl); -#endif - - UnsafeUtility.CallFunctionPtr_pp(marshalToBurstFnPtr.ToPointer(), dst, src); - - // Since we are running inline, normally the outer job scheduling code would - // reference jobWrapper.Data however we can't do that since if we are in this code it means - // we are dealing with a job/jobwrapper that is burst compiled and is non-blittable. Thus we need to get - // the JobData ptr from what Burst sees which is what we do here. - var jobDataPtr = GetJobChunkJobDataOffset() + alignedUnmanagedJobWrapper; - functionPointer(ref query, limitToEntityArrayPtr, limitToEntityArrayLength, (IntPtr) jobDataPtr); - - // Since Run can capture locals for write back, we must write back the marshalled jobData after the job executes - var marshalFromBurstFnPtr = JobMarshalFnLookup.GetMarshalFromBurstFn(); - UnsafeUtility.CallFunctionPtr_pp(marshalFromBurstFnPtr.ToPointer(), src, dst); - -#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - impl->_Access->DependencyManager->ForEachStructuralChange.EndIsInForEach(); -#endif - - jobData = jobStructData.JobData; - } - else - { - functionPointer(ref query, limitToEntityArrayPtr, limitToEntityArrayLength, (IntPtr) managedJobDataPtr); - } - } - } - - public static void UnsafeRunJobChunk(ref T jobData, EntityQuery query, IntPtr limitToEntityArrayPtr, int limitToEntityArrayLength, - FunctionPointer functionPointer) - where T : struct, IJobChunk, IJobBase - { - // Keep the API 'safe' so that only Entities.dll needs to be compiled with /unsafe - unsafe - { - var managedJobDataPtr = UnsafeUtility.AddressOf(ref jobData); - var unmanagedSize = jobData.GetUnmanagedJobSize_Gen(); - if (unmanagedSize != -1) - { - const int kAlignment = 16; - int alignedSize = (unmanagedSize + kAlignment - 1) & ~(kAlignment - 1); - byte* unmanagedJobWrapper = stackalloc byte[alignedSize]; - byte* alignedUnmanagedJobWrapper = (byte*) ((UInt64) (unmanagedJobWrapper + kAlignment - 1) & ~(UInt64) (kAlignment - 1)); - - // DOTS Runtime job marshalling code assumes the job is wrapped so create the wrapper and assign the jobData - - JobChunkExtensions.JobChunkWrapper jobStructData = default; - jobStructData.JobData = jobData; - byte* jobStructDataPtr = (byte*) UnsafeUtility.AddressOf(ref jobStructData); - - byte* dst = alignedUnmanagedJobWrapper; - byte* src = jobStructDataPtr; - var marshalToBurstFnPtr = JobMarshalFnLookup.GetMarshalToBurstFn(); - -#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - var impl = query._GetImpl(); - impl->_Access->DependencyManager->ForEachStructuralChange.BeginIsInForEach(impl); -#endif - UnsafeUtility.CallFunctionPtr_pp(marshalToBurstFnPtr.ToPointer(), dst, src); - - // Since we are running inline, normally the outer job scheduling code would - // reference jobWrapper.Data however we can't do that since if we are in this code it means - // we are dealing with a job/jobwrapper that is burst compiled and is non-blittable. Thus we need to get - // the JobData ptr from what Burst sees which is what we do here. - var jobDataPtr = GetJobChunkJobDataOffset() + alignedUnmanagedJobWrapper; - functionPointer.Invoke(ref query, limitToEntityArrayPtr, limitToEntityArrayLength, (IntPtr) jobDataPtr); - - // Since Run can capture locals for write back, we must write back the marshalled jobData after the job executes - var marshalFromBurstFnPtr = JobMarshalFnLookup.GetMarshalFromBurstFn(); - UnsafeUtility.CallFunctionPtr_pp(marshalFromBurstFnPtr.ToPointer(), src, dst); - -#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - impl->_Access->DependencyManager->ForEachStructuralChange.EndIsInForEach(); -#endif - - jobData = jobStructData.JobData; - } - else - { - functionPointer.Invoke(ref query, limitToEntityArrayPtr, limitToEntityArrayLength, (IntPtr) managedJobDataPtr); - } - } - } - } -} - -#endif // #if UNITY_DOTSRUNTIME &&!UNITY_DOTSRUNTIME_IL2CPP diff --git a/Unity.Entities/Internal/InternalCompilerInterface_RunMethods_RT.cs.meta b/Unity.Entities/Internal/InternalCompilerInterface_RunMethods_RT.cs.meta deleted file mode 100644 index e4f9500..0000000 --- a/Unity.Entities/Internal/InternalCompilerInterface_RunMethods_RT.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: d079506881ce4a04dab0438d5c9e5298 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities/Internal/InternalEntityQueryEnumerator.cs b/Unity.Entities/Internal/InternalEntityQueryEnumerator.cs index 58a9d9b..3f5d222 100644 --- a/Unity.Entities/Internal/InternalEntityQueryEnumerator.cs +++ b/Unity.Entities/Internal/InternalEntityQueryEnumerator.cs @@ -143,10 +143,11 @@ public bool MoveNextEntityRange(out bool movedToNewChunk, out ArchetypeChunk chu if (_QueryHasFilterOrEnableable == 0) { #if UNITY_BURST_EXPERIMENTAL_PREFETCH_INTRINSIC - if (Burst.CompilerServices.Hint.Likely(_ChunkIndex + 1 < _CachedChunkListLength)) - Common.Prefetch(_CachedChunkList.Ptr[_ChunkIndex + 1], Common.ReadWrite.Read); + if (Hint.Likely(_ChunkIndex + 1 < _CachedChunkListLength)) + { + Common.Prefetch(&EntityComponentStore.PerChunkArray.ChunkData[_CachedChunkList.MatchingChunks->Ptr[_ChunkIndex + 1]], Common.ReadWrite.Read); + } #endif - _ChunkIndex++; // If we have iterated through all the chunks @@ -160,7 +161,7 @@ public bool MoveNextEntityRange(out bool movedToNewChunk, out ArchetypeChunk chu return false; } - chunk = new ArchetypeChunk(_CachedChunkList.Ptr[_ChunkIndex], _CachedChunkList.EntityComponentStore); + chunk = new ArchetypeChunk(_CachedChunkList.ChunkIndices[_ChunkIndex], _CachedChunkList.EntityComponentStore); movedToNewChunk = true; entityStartIndex = 0; entityEndIndex = chunk.Count; @@ -228,7 +229,7 @@ public bool MoveNextColdLoop(out ArchetypeChunk chunk) return false; } - chunk = new ArchetypeChunk(_CachedChunkList.Ptr[_ChunkIndex], _CachedChunkList.EntityComponentStore); + chunk = new ArchetypeChunk(_CachedChunkList.ChunkIndices[_ChunkIndex], _CachedChunkList.EntityComponentStore); IndexInChunk = 0; _EndIndexInChunk = chunk.Count; return true; diff --git a/Unity.Entities/Iterators/ArchetypeChunkArray.cs b/Unity.Entities/Iterators/ArchetypeChunkArray.cs index 93bb79e..09d632a 100644 --- a/Unity.Entities/Iterators/ArchetypeChunkArray.cs +++ b/Unity.Entities/Iterators/ArchetypeChunkArray.cs @@ -19,33 +19,33 @@ namespace Unity.Entities public unsafe struct ArchetypeChunk : IEquatable { [FieldOffset(0)] - [NativeDisableUnsafePtrRestriction] internal Chunk* m_Chunk; + [NativeDisableUnsafePtrRestriction] internal ChunkIndex m_Chunk; [FieldOffset(8)] [NativeDisableUnsafePtrRestriction] internal EntityComponentStore* m_EntityComponentStore; /// /// Returns the number of entities in the chunk. /// - public readonly int Count => m_Chunk->Count; + public readonly int Count => m_Chunk.Count; /// /// The number of entities currently stored in the chunk. /// [Obsolete("This property is always equal to Count, and will be removed in a future release.")] - public readonly int ChunkEntityCount => m_Chunk->Count; + public readonly int ChunkEntityCount => m_Chunk.Count; /// /// The number of entities that can fit in this chunk. /// /// The capacity of a chunk depends on the size of the components making up the /// of the entities stored in the chunk. - public readonly int Capacity => m_Chunk->Capacity; + public readonly int Capacity => m_EntityComponentStore->GetArchetype(m_Chunk)->ChunkCapacity; /// /// Whether this chunk is exactly full. /// public readonly bool Full => Count == Capacity; - internal ArchetypeChunk(Chunk* chunk, EntityComponentStore* entityComponentStore) + internal ArchetypeChunk(ChunkIndex chunk, EntityComponentStore* entityComponentStore) { m_Chunk = chunk; m_EntityComponentStore = entityComponentStore; @@ -89,13 +89,7 @@ public override bool Equals(object compare) /// Computes a hashcode to support hash-based collections. /// /// The computed hash. - public readonly override int GetHashCode() - { - UIntPtr chunkAddr = (UIntPtr)m_Chunk; - long chunkHiHash = ((long)chunkAddr) >> 15; - int chunkHash = (int)chunkHiHash; - return chunkHash; - } + public readonly override int GetHashCode() => m_Chunk; /// /// The archetype of the entities stored in this chunk. @@ -104,13 +98,13 @@ public readonly override int GetHashCode() public readonly EntityArchetype Archetype => new EntityArchetype() { - Archetype = m_Chunk->Archetype, + Archetype = m_EntityComponentStore->GetArchetype(m_Chunk) }; /// /// SequenceNumber is a unique number for each chunk, across all worlds. /// - public ulong SequenceNumber => m_Chunk->SequenceNumber; + public ulong SequenceNumber => m_Chunk.SequenceNumber; /// /// A special "null" ArchetypeChunk that you can use to test whether ArchetypeChunk instances are valid. @@ -136,7 +130,7 @@ public bool Equals(ArchetypeChunk archetypeChunk) /// The shared component count. public readonly int NumSharedComponents() { - return m_Chunk->Archetype->NumSharedComponents; + return m_EntityComponentStore->GetArchetype(m_Chunk)->NumSharedComponents; } /// @@ -146,7 +140,7 @@ public readonly int NumSharedComponents() /// instance. public readonly bool Invalid() { - return m_Chunk->Archetype == null; + return m_EntityComponentStore->GetArchetype(m_Chunk) == null; } /// @@ -162,8 +156,8 @@ public readonly NativeArray GetNativeArray(EntityTypeHandle entityTypeHa #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(entityTypeHandle.m_Safety); #endif - var archetype = m_Chunk->Archetype; - var buffer = m_Chunk->Buffer; + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + var buffer = m_Chunk.Buffer; var length = Count; var startOffset = archetype->Offsets[0]; var result = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(buffer + startOffset, length, Allocator.None); @@ -406,13 +400,15 @@ public readonly bool DidChange(DynamicSharedComponentTypeHandle typeHandle, uint public readonly uint GetChangeVersion(ref ComponentTypeHandle typeHandle) where T : IComponentData { - if (Hint.Unlikely(typeHandle.m_LookupCache.Archetype != m_Chunk->Archetype)) + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + if (Hint.Unlikely(typeHandle.m_LookupCache.Archetype != archetype)) { - typeHandle.m_LookupCache.Update(m_Chunk->Archetype, typeHandle.m_TypeIndex); + typeHandle.m_LookupCache.Update(archetype, typeHandle.m_TypeIndex); } var typeIndexInArchetype = typeHandle.m_LookupCache.IndexInArchetype; - if (Hint.Unlikely((typeIndexInArchetype == -1))) return 0; - return m_Chunk->GetChangeVersion(typeIndexInArchetype); + if (Hint.Unlikely(typeIndexInArchetype == -1)) return 0; + + return Archetype.Archetype->Chunks.GetChangeVersion(typeIndexInArchetype, m_Chunk.ListIndex); } /// /// Obsolete. Use instead. @@ -458,10 +454,11 @@ public readonly uint GetChangeVersion(ComponentTypeHandle typeHandle) /// a component of the specified type. public readonly uint GetChangeVersion(ref DynamicComponentTypeHandle typeHandle) { - ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); + ChunkDataUtility.GetIndexInTypeArray(m_EntityComponentStore->GetArchetype(m_Chunk), typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); int typeIndexInArchetype = typeHandle.m_TypeLookupCache; if (Hint.Unlikely(typeIndexInArchetype == -1)) return 0; - return m_Chunk->GetChangeVersion(typeIndexInArchetype); + + return Archetype.Archetype->Chunks.GetChangeVersion(typeIndexInArchetype, m_Chunk.ListIndex); } /// /// Obsolete. Use instead. @@ -507,13 +504,15 @@ public readonly uint GetChangeVersion(DynamicComponentTypeHandle typeHandle) public readonly uint GetChangeVersion(ref BufferTypeHandle bufferTypeHandle) where T : unmanaged, IBufferElementData { - if (Hint.Unlikely(bufferTypeHandle.m_LookupCache.Archetype != m_Chunk->Archetype)) + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + if (Hint.Unlikely(bufferTypeHandle.m_LookupCache.Archetype != archetype)) { - bufferTypeHandle.m_LookupCache.Update(m_Chunk->Archetype, bufferTypeHandle.m_TypeIndex); + bufferTypeHandle.m_LookupCache.Update(archetype, bufferTypeHandle.m_TypeIndex); } var typeIndexInArchetype = bufferTypeHandle.m_LookupCache.IndexInArchetype; if (Hint.Unlikely(typeIndexInArchetype == -1)) return 0; - return m_Chunk->GetChangeVersion(typeIndexInArchetype); + + return Archetype.Archetype->Chunks.GetChangeVersion(typeIndexInArchetype, m_Chunk.ListIndex); } /// /// Obsolete. Use instead. @@ -553,9 +552,10 @@ public readonly uint GetChangeVersion(BufferTypeHandle bufferTypeHandle) public readonly uint GetChangeVersion(SharedComponentTypeHandle chunkSharedComponentData) where T : struct, ISharedComponentData { - var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, chunkSharedComponentData.m_TypeIndex); + var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(m_EntityComponentStore->GetArchetype(m_Chunk), chunkSharedComponentData.m_TypeIndex); if (Hint.Unlikely(typeIndexInArchetype == -1)) return 0; - return m_Chunk->GetChangeVersion(typeIndexInArchetype); + + return Archetype.Archetype->Chunks.GetChangeVersion(typeIndexInArchetype, m_Chunk.ListIndex); } /// @@ -576,11 +576,12 @@ public readonly uint GetChangeVersion(SharedComponentTypeHandle chunkShare /// a shared component of the specified type. public readonly uint GetChangeVersion(ref DynamicSharedComponentTypeHandle typeHandle) { - ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, typeHandle.m_TypeIndex, + ChunkDataUtility.GetIndexInTypeArray(m_EntityComponentStore->GetArchetype(m_Chunk), typeHandle.m_TypeIndex, ref typeHandle.m_cachedTypeIndexinArchetype); int typeIndexInArchetype = typeHandle.m_cachedTypeIndexinArchetype; if (Hint.Unlikely(typeIndexInArchetype == -1)) return 0; - return m_Chunk->GetChangeVersion(typeIndexInArchetype); + + return Archetype.Archetype->Chunks.GetChangeVersion(typeIndexInArchetype, m_Chunk.ListIndex); } /// /// Obsolete. Use instead. @@ -624,7 +625,7 @@ public readonly bool DidOrderChange(uint version) /// The current order version of this chunk. public readonly uint GetOrderVersion() { - return m_Chunk->GetOrderVersion(); + return Archetype.Archetype->Chunks.GetOrderVersion(m_Chunk.ListIndex); } /// @@ -651,9 +652,10 @@ public readonly bool IsComponentEnabled(ref ComponentTypeHandle typeHandle #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(typeHandle.m_Safety); #endif - if (Hint.Unlikely(typeHandle.m_LookupCache.Archetype != m_Chunk->Archetype)) + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + if (Hint.Unlikely(typeHandle.m_LookupCache.Archetype != archetype)) { - typeHandle.m_LookupCache.Update(m_Chunk->Archetype, typeHandle.m_TypeIndex); + typeHandle.m_LookupCache.Update(archetype, typeHandle.m_TypeIndex); } var typeIndexInArchetype = typeHandle.m_LookupCache.IndexInArchetype; if (Hint.Unlikely(typeIndexInArchetype == -1)) @@ -700,9 +702,10 @@ public readonly bool IsComponentEnabled(ref BufferTypeHandle bufferTypeHan #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(bufferTypeHandle.m_Safety0); #endif - if (Hint.Unlikely(bufferTypeHandle.m_LookupCache.Archetype != m_Chunk->Archetype)) + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + if (Hint.Unlikely(bufferTypeHandle.m_LookupCache.Archetype != archetype)) { - bufferTypeHandle.m_LookupCache.Update(m_Chunk->Archetype, bufferTypeHandle.m_TypeIndex); + bufferTypeHandle.m_LookupCache.Update(archetype, bufferTypeHandle.m_TypeIndex); } var typeIndexInArchetype = bufferTypeHandle.m_LookupCache.IndexInArchetype; if (Hint.Unlikely(typeIndexInArchetype == -1)) @@ -740,7 +743,7 @@ public readonly bool IsComponentEnabled(ref DynamicComponentTypeHandle typeHandl #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(typeHandle.m_Safety0); #endif - ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); + ChunkDataUtility.GetIndexInTypeArray(m_EntityComponentStore->GetArchetype(m_Chunk), typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); var typeIndexInArchetype = typeHandle.m_TypeLookupCache; if (Hint.Unlikely(typeIndexInArchetype == -1)) return false; @@ -763,7 +766,7 @@ public readonly void SetComponentEnabled(ref DynamicComponentTypeHandle typeHand #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckWriteAndThrow(typeHandle.m_Safety0); #endif - ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); + ChunkDataUtility.GetIndexInTypeArray(m_EntityComponentStore->GetArchetype(m_Chunk), typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); var typeIndexInArchetype = typeHandle.m_TypeLookupCache; #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG if (Hint.Unlikely(typeIndexInArchetype == -1)) @@ -787,16 +790,16 @@ public readonly void SetComponentEnabled(ref DynamicComponentTypeHandle typeHand /// A is returned containing a copy of the bitarray. public readonly unsafe v128 GetEnableableBits(ref DynamicComponentTypeHandle handle) { - var chunks = m_Chunk->Archetype->Chunks; - ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, handle.m_TypeIndex, ref handle.m_TypeLookupCache); + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + ChunkDataUtility.GetIndexInTypeArray(archetype, handle.m_TypeIndex, ref handle.m_TypeLookupCache); if (handle.m_TypeLookupCache == -1) return default; int indexInArchetype = handle.m_TypeLookupCache; - int memoryOrderIndexInArchetype = m_Chunk->Archetype->TypeIndexInArchetypeToMemoryOrderIndex[indexInArchetype]; + int memoryOrderIndexInArchetype = archetype->TypeIndexInArchetypeToMemoryOrderIndex[indexInArchetype]; return handle.m_TypeLookupCache == -1 ? new v128() : - *chunks.GetComponentEnabledMaskArrayForTypeInChunk(memoryOrderIndexInArchetype, m_Chunk->ListIndex); + *archetype->Chunks.GetComponentEnabledMaskArrayForTypeInChunk(memoryOrderIndexInArchetype, m_Chunk.ListIndex); } /// @@ -811,9 +814,10 @@ public readonly EnabledMask GetEnabledMask(ref ComponentTypeHandle typeHan #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(typeHandle.m_Safety); #endif - if (Hint.Unlikely(typeHandle.m_LookupCache.Archetype != m_Chunk->Archetype)) + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + if (Hint.Unlikely(typeHandle.m_LookupCache.Archetype != archetype)) { - typeHandle.m_LookupCache.Update(m_Chunk->Archetype, typeHandle.m_TypeIndex); + typeHandle.m_LookupCache.Update(archetype, typeHandle.m_TypeIndex); } // In case the chunk does not contains the component type (or the internal TypeIndex lookup fails to find a // match), the LookupCache.Update will invalidate the IndexInArchetype. @@ -828,8 +832,8 @@ public readonly EnabledMask GetEnabledMask(ref ComponentTypeHandle typeHan } int* ptrChunkDisabledCount = default; var ptr = (typeHandle.IsReadOnly) - ? ChunkDataUtility.GetEnabledRefRO(m_Chunk, typeHandle.m_LookupCache.IndexInArchetype).Ptr - : ChunkDataUtility.GetEnabledRefRW(m_Chunk, typeHandle.m_LookupCache.IndexInArchetype, + ? ChunkDataUtility.GetEnabledRefRO(m_Chunk, Archetype.Archetype, typeHandle.m_LookupCache.IndexInArchetype).Ptr + : ChunkDataUtility.GetEnabledRefRW(m_Chunk, Archetype.Archetype, typeHandle.m_LookupCache.IndexInArchetype, typeHandle.GlobalSystemVersion, out ptrChunkDisabledCount).Ptr; #if ENABLE_UNITY_COLLECTIONS_CHECKS var result = new EnabledMask(new SafeBitRef(ptr, 0, typeHandle.m_Safety), ptrChunkDisabledCount); @@ -864,9 +868,10 @@ public readonly void SetComponentEnabled(ref ComponentTypeHandle typeHandl #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckWriteAndThrow(typeHandle.m_Safety); #endif - if (Hint.Unlikely(typeHandle.m_LookupCache.Archetype != m_Chunk->Archetype)) + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + if (Hint.Unlikely(typeHandle.m_LookupCache.Archetype != archetype)) { - typeHandle.m_LookupCache.Update(m_Chunk->Archetype, typeHandle.m_TypeIndex); + typeHandle.m_LookupCache.Update(archetype, typeHandle.m_TypeIndex); } var typeIndexInArchetype = typeHandle.m_LookupCache.IndexInArchetype; #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG @@ -923,9 +928,10 @@ public readonly void SetComponentEnabled(ref BufferTypeHandle bufferTypeHa #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckWriteAndThrow(bufferTypeHandle.m_Safety0); #endif - if (Hint.Unlikely(bufferTypeHandle.m_LookupCache.Archetype != m_Chunk->Archetype)) + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + if (Hint.Unlikely(bufferTypeHandle.m_LookupCache.Archetype != archetype)) { - bufferTypeHandle.m_LookupCache.Update(m_Chunk->Archetype, bufferTypeHandle.m_TypeIndex); + bufferTypeHandle.m_LookupCache.Update(archetype, bufferTypeHandle.m_TypeIndex); } var typeIndexInArchetype = bufferTypeHandle.m_LookupCache.IndexInArchetype; #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG @@ -972,8 +978,9 @@ public readonly T GetChunkComponentData(ref ComponentTypeHandle typeHandle AtomicSafetyHandle.CheckReadAndThrow(typeHandle.m_Safety); #endif // TODO(DOTS-5748): use type handle's LookupCache here - m_EntityComponentStore->AssertEntityHasComponent(m_Chunk->metaChunkEntity, typeHandle.m_TypeIndex); - var ptr = m_EntityComponentStore->GetComponentDataWithTypeRO(m_Chunk->metaChunkEntity, typeHandle.m_TypeIndex); + var metaChunkEntity = m_Chunk.MetaChunkEntity; + m_EntityComponentStore->AssertEntityHasComponent(metaChunkEntity, typeHandle.m_TypeIndex); + var ptr = m_EntityComponentStore->GetComponentDataWithTypeRO(metaChunkEntity, typeHandle.m_TypeIndex); T value; UnsafeUtility.CopyPtrToStructure(ptr, out value); return value; @@ -1009,8 +1016,9 @@ public readonly void SetChunkComponentData(ref ComponentTypeHandle typeHan AtomicSafetyHandle.CheckWriteAndThrow(typeHandle.m_Safety); #endif // TODO(DOTS-5748): use type handle's LookupCache here - m_EntityComponentStore->AssertEntityHasComponent(m_Chunk->metaChunkEntity, typeHandle.m_TypeIndex); - var ptr = m_EntityComponentStore->GetComponentDataWithTypeRW(m_Chunk->metaChunkEntity, typeHandle.m_TypeIndex, typeHandle.GlobalSystemVersion); + var metaChunkEntity = m_Chunk.MetaChunkEntity; + m_EntityComponentStore->AssertEntityHasComponent(metaChunkEntity, typeHandle.m_TypeIndex); + var ptr = m_EntityComponentStore->GetComponentDataWithTypeRW(metaChunkEntity, typeHandle.m_TypeIndex, typeHandle.GlobalSystemVersion); UnsafeUtility.CopyStructureToPtr(ref value, ptr); } /// @@ -1047,12 +1055,12 @@ public readonly void SetChunkComponentData(ComponentTypeHandle typeHandle, public readonly int GetSharedComponentIndex(SharedComponentTypeHandle chunkSharedComponentData) where T : struct, ISharedComponentData { - var archetype = m_Chunk->Archetype; + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(archetype, chunkSharedComponentData.m_TypeIndex); if (Hint.Unlikely(typeIndexInArchetype == -1)) return -1; var chunkSharedComponentIndex = typeIndexInArchetype - archetype->FirstSharedComponent; - var sharedComponentIndex = m_Chunk->GetSharedComponentValue(chunkSharedComponentIndex); + var sharedComponentIndex = archetype->Chunks.GetSharedComponentValue(chunkSharedComponentIndex, m_Chunk.ListIndex); return sharedComponentIndex; } @@ -1075,14 +1083,14 @@ public readonly int GetSharedComponentIndex(ref DynamicSharedComponentTypeHandle #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(typeHandle.m_Safety); #endif - var archetype = m_Chunk->Archetype; + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); ChunkDataUtility.GetIndexInTypeArray(archetype, typeHandle.m_TypeIndex, ref typeHandle.m_cachedTypeIndexinArchetype); var typeIndexInArchetype = typeHandle.m_cachedTypeIndexinArchetype; if (Hint.Unlikely(typeIndexInArchetype == -1)) return -1; var chunkSharedComponentIndex = typeIndexInArchetype - archetype->FirstSharedComponent; - var sharedComponentIndex = m_Chunk->GetSharedComponentValue(chunkSharedComponentIndex); + var sharedComponentIndex = archetype->Chunks.GetSharedComponentValue(chunkSharedComponentIndex, m_Chunk.ListIndex); return sharedComponentIndex; } /// @@ -1242,9 +1250,10 @@ public readonly bool Has(ref ComponentTypeHandle typeHandle) , new() #endif { - if (Hint.Unlikely(typeHandle.m_LookupCache.Archetype != m_Chunk->Archetype)) + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + if (Hint.Unlikely(typeHandle.m_LookupCache.Archetype != archetype)) { - typeHandle.m_LookupCache.Update(m_Chunk->Archetype, typeHandle.m_TypeIndex); + typeHandle.m_LookupCache.Update(archetype, typeHandle.m_TypeIndex); } var typeIndexInArchetype = typeHandle.m_LookupCache.IndexInArchetype; return (typeIndexInArchetype != -1); @@ -1283,7 +1292,7 @@ public readonly bool Has(ComponentTypeHandle typeHandle) /// True, if this chunk contains an array of the specified component type. public readonly bool Has() { - var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, TypeManager.GetTypeIndex()); + var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(m_EntityComponentStore->GetArchetype(m_Chunk), TypeManager.GetTypeIndex()); return (typeIndexInArchetype != -1); } @@ -1294,7 +1303,7 @@ public readonly bool Has() /// True, if this chunk contains an array of the specified component type. public readonly bool Has(ref DynamicComponentTypeHandle typeHandle) { - ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); + ChunkDataUtility.GetIndexInTypeArray(m_EntityComponentStore->GetArchetype(m_Chunk), typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); return (typeHandle.m_TypeLookupCache != -1); } /// @@ -1325,11 +1334,11 @@ public readonly bool Has(DynamicComponentTypeHandle typeHandle) public readonly bool HasChunkComponent(ref ComponentTypeHandle typeHandle) where T : unmanaged, IComponentData { - var metaChunkArchetype = m_Chunk->Archetype->MetaChunkArchetype; + var metaChunkArchetype = m_EntityComponentStore->GetArchetype(m_Chunk)->MetaChunkArchetype; if (Hint.Unlikely(metaChunkArchetype == null)) return false; // TODO(DOTS-5748): use type handle's LookupCache here - var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype->MetaChunkArchetype, typeHandle.m_TypeIndex); + var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(metaChunkArchetype, typeHandle.m_TypeIndex); return (typeIndexInArchetype != -1); } /// @@ -1360,10 +1369,10 @@ public readonly bool HasChunkComponent(ComponentTypeHandle typeHandle) public readonly bool HasChunkComponent() where T : unmanaged, IComponentData { - var metaChunkArchetype = m_Chunk->Archetype->MetaChunkArchetype; + var metaChunkArchetype = m_EntityComponentStore->GetArchetype(m_Chunk)->MetaChunkArchetype; if (Hint.Unlikely(metaChunkArchetype == null)) return false; - var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype->MetaChunkArchetype, TypeManager.GetTypeIndex()); + var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(metaChunkArchetype, TypeManager.GetTypeIndex()); return (typeIndexInArchetype != -1); } @@ -1383,7 +1392,7 @@ public readonly bool HasChunkComponent() public readonly bool Has(SharedComponentTypeHandle typeHandle) where T : struct, ISharedComponentData { - var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, typeHandle.m_TypeIndex); + var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(m_EntityComponentStore->GetArchetype(m_Chunk), typeHandle.m_TypeIndex); return (typeIndexInArchetype != -1); } @@ -1399,7 +1408,7 @@ public readonly bool Has(SharedComponentTypeHandle typeHandle) /// True, if this chunk contains a shared component of the specified type. public readonly bool Has(ref DynamicSharedComponentTypeHandle typeHandle) { - ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, typeHandle.m_TypeIndex, + ChunkDataUtility.GetIndexInTypeArray(m_EntityComponentStore->GetArchetype(m_Chunk), typeHandle.m_TypeIndex, ref typeHandle.m_cachedTypeIndexinArchetype); return (typeHandle.m_cachedTypeIndexinArchetype != -1); } @@ -1434,9 +1443,10 @@ public readonly bool Has(DynamicSharedComponentTypeHandle typeHandle) public readonly bool Has(ref BufferTypeHandle bufferTypeHandle) where T : unmanaged, IBufferElementData { - if (Hint.Unlikely(bufferTypeHandle.m_LookupCache.Archetype != m_Chunk->Archetype)) + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + if (Hint.Unlikely(bufferTypeHandle.m_LookupCache.Archetype != archetype)) { - bufferTypeHandle.m_LookupCache.Update(m_Chunk->Archetype, bufferTypeHandle.m_TypeIndex); + bufferTypeHandle.m_LookupCache.Update(archetype, bufferTypeHandle.m_TypeIndex); } var typeIndexInArchetype = bufferTypeHandle.m_LookupCache.IndexInArchetype; return (typeIndexInArchetype != -1); @@ -1475,7 +1485,7 @@ public readonly NativeArray GetNativeArray(ref ComponentTypeHandle type #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(typeHandle.m_Safety); #endif - var archetype = m_Chunk->Archetype; + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); byte* ptr = (typeHandle.IsReadOnly) ? ChunkDataUtility.GetOptionalComponentDataWithTypeRO(m_Chunk, archetype, 0, typeHandle.m_TypeIndex, ref typeHandle.m_LookupCache) @@ -1530,8 +1540,8 @@ public readonly NativeArray GetNativeArray(ComponentTypeHandle typeHand #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(entityTypeHandle.m_Safety); #endif - var archetype = m_Chunk->Archetype; - var buffer = m_Chunk->Buffer; + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + var buffer = m_Chunk.Buffer; var startOffset = archetype->Offsets[0]; var result = buffer + startOffset; @@ -1558,7 +1568,7 @@ public readonly NativeArray GetNativeArray(ComponentTypeHandle typeHand // This updates the type handle's cache as a side effect, which will tell us if the archetype has the component // or not. - void* ptr = ChunkDataUtility.GetOptionalComponentDataWithTypeRO(m_Chunk, m_Chunk->Archetype, 0, + void* ptr = ChunkDataUtility.GetOptionalComponentDataWithTypeRO(m_Chunk, m_EntityComponentStore->GetArchetype(m_Chunk), 0, typeHandle.m_TypeIndex, ref typeHandle.m_LookupCache); return (T*)ptr; } @@ -1587,7 +1597,7 @@ public readonly NativeArray GetNativeArray(ComponentTypeHandle typeHand "Provided ComponentTypeHandle is read-only; can't get a read/write pointer to component data"); #endif - byte* ptr = ChunkDataUtility.GetOptionalComponentDataWithTypeRW(m_Chunk, m_Chunk->Archetype, + byte* ptr = ChunkDataUtility.GetOptionalComponentDataWithTypeRW(m_Chunk, m_EntityComponentStore->GetArchetype(m_Chunk), 0, typeHandle.m_TypeIndex, typeHandle.GlobalSystemVersion, ref typeHandle.m_LookupCache); @@ -1619,7 +1629,7 @@ public readonly NativeArray GetNativeArray(ComponentTypeHandle typeHand AtomicSafetyHandle.CheckReadAndThrow(typeHandle.m_Safety); #endif - byte* ptr = ChunkDataUtility.GetComponentDataWithTypeRO(m_Chunk, m_Chunk->Archetype, 0, + byte* ptr = ChunkDataUtility.GetComponentDataWithTypeRO(m_Chunk, m_EntityComponentStore->GetArchetype(m_Chunk), 0, typeHandle.m_TypeIndex, ref typeHandle.m_LookupCache); #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG // Must check this after computing the pointer, to make sure the cache is up to date @@ -1651,7 +1661,7 @@ public readonly NativeArray GetNativeArray(ComponentTypeHandle typeHand #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckWriteAndThrow(typeHandle.m_Safety); #endif - byte* ptr = ChunkDataUtility.GetComponentDataWithTypeRW(m_Chunk, m_Chunk->Archetype, + byte* ptr = ChunkDataUtility.GetComponentDataWithTypeRW(m_Chunk, m_EntityComponentStore->GetArchetype(m_Chunk), 0, typeHandle.m_TypeIndex, typeHandle.GlobalSystemVersion, ref typeHandle.m_LookupCache); #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG @@ -1676,7 +1686,7 @@ public readonly NativeArray GetNativeArray(ComponentTypeHandle typeHand } [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] - private static void CheckZeroSizedGetDynamicComponentDataArrayReinterpret(in DynamicComponentTypeHandle typeHandle) + private static void CheckZeroSizedGetDynamicComponentDataArrayReinterpret(in DynamicComponentTypeHandle typeHandle) { if (Hint.Unlikely(typeHandle.IsZeroSized)) { @@ -1720,12 +1730,12 @@ private static void CheckCannotBeAliasedDueToSizeConstraints(in DynamicCompon public readonly NativeArray GetDynamicComponentDataArrayReinterpret(ref DynamicComponentTypeHandle typeHandle, int expectedTypeSize) where T : struct { - CheckZeroSizedGetDynamicComponentDataArrayReinterpret(typeHandle); + CheckZeroSizedGetDynamicComponentDataArrayReinterpret(typeHandle); #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(typeHandle.m_Safety0); #endif - var archetype = m_Chunk->Archetype; - ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + ChunkDataUtility.GetIndexInTypeArray(archetype, typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); var typeIndexInArchetype = typeHandle.m_TypeLookupCache; if (Hint.Unlikely(typeIndexInArchetype == -1)) { @@ -1755,8 +1765,8 @@ public readonly NativeArray GetDynamicComponentDataArrayReinterpret(ref Dy CheckCannotBeAliasedDueToSizeConstraints(typeHandle, outTypeSize, outLength, byteLen, length); byte* ptr = (typeHandle.IsReadOnly) - ? ChunkDataUtility.GetComponentDataRO(m_Chunk, 0, typeIndexInArchetype) - : ChunkDataUtility.GetComponentDataRW(m_Chunk, 0, typeIndexInArchetype, typeHandle.GlobalSystemVersion); + ? ChunkDataUtility.GetComponentDataRO(m_Chunk, archetype, 0, typeIndexInArchetype) + : ChunkDataUtility.GetComponentDataRW(m_Chunk, archetype, 0, typeIndexInArchetype, typeHandle.GlobalSystemVersion); var result = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray(ptr, outLength, Allocator.None); #if ENABLE_UNITY_COLLECTIONS_CHECKS NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref result, typeHandle.m_Safety0); @@ -1769,6 +1779,36 @@ public readonly NativeArray GetDynamicComponentDataArrayReinterpret(ref Dy return result; } + + //Method used by EntityBehaviour in motion + internal readonly void* GetDynamicComponentDataPtr(ref DynamicComponentTypeHandle typeHandle) + { + CheckZeroSizedGetDynamicComponentDataArrayReinterpret(typeHandle); +#if ENABLE_UNITY_COLLECTIONS_CHECKS + AtomicSafetyHandle.CheckReadAndThrow(typeHandle.m_Safety0); +#endif + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); + ChunkDataUtility.GetIndexInTypeArray(archetype, typeHandle.m_TypeIndex, ref typeHandle.m_TypeLookupCache); + var typeIndexInArchetype = typeHandle.m_TypeLookupCache; + if (Hint.Unlikely(typeIndexInArchetype == -1)) + { + return null; + } + +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(archetype->Types[typeIndexInArchetype].IsBuffer)) + { + var typeName = typeHandle.m_TypeIndex.ToFixedString(); + throw new ArgumentException($"ArchetypeChunk.GetDynamicComponentDataArrayReinterpret cannot be called for IBufferElementData {typeName}"); + } +#endif + byte* ptr = (typeHandle.IsReadOnly) + ? ChunkDataUtility.GetComponentDataRO(m_Chunk, archetype, 0, typeIndexInArchetype) + : ChunkDataUtility.GetComponentDataRW(m_Chunk, archetype, 0, typeIndexInArchetype, typeHandle.GlobalSystemVersion); + + return ptr; + } + /// /// Obsolete. Use instead. /// @@ -1798,7 +1838,7 @@ public readonly ManagedComponentAccessor GetManagedComponentAccessor(ref C #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(typeHandle.m_Safety); #endif - var archetype = m_Chunk->Archetype; + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); byte* ptr = ChunkDataUtility.GetOptionalComponentDataWithTypeRW(m_Chunk, archetype, 0, typeHandle.m_TypeIndex, typeHandle.GlobalSystemVersion, ref typeHandle.m_LookupCache); @@ -1850,11 +1890,11 @@ public readonly BufferAccessor GetBufferAccessor(ref BufferTypeHandle b #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(bufferTypeHandle.m_Safety0); #endif - var archetype = m_Chunk->Archetype; + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); var typeIndex = bufferTypeHandle.m_TypeIndex; if (Hint.Unlikely(bufferTypeHandle.m_LookupCache.Archetype != archetype)) { - bufferTypeHandle.m_LookupCache.Update(m_Chunk->Archetype, typeIndex); + bufferTypeHandle.m_LookupCache.Update(m_EntityComponentStore->GetArchetype(m_Chunk), typeIndex); } byte* ptr = (bufferTypeHandle.IsReadOnly) @@ -1911,9 +1951,9 @@ public readonly LowLevel.Unsafe.UnsafeUntypedBufferAccessor GetUntypedBufferAcce #if ENABLE_UNITY_COLLECTIONS_CHECKS AtomicSafetyHandle.CheckReadAndThrow(chunkBufferTypeHandle.m_Safety0); #endif - var archetype = m_Chunk->Archetype; + var archetype = m_EntityComponentStore->GetArchetype(m_Chunk); short typeIndexInArchetype = chunkBufferTypeHandle.m_TypeLookupCache; - ChunkDataUtility.GetIndexInTypeArray(m_Chunk->Archetype, chunkBufferTypeHandle.m_TypeIndex, ref typeIndexInArchetype); + ChunkDataUtility.GetIndexInTypeArray(archetype, chunkBufferTypeHandle.m_TypeIndex, ref typeIndexInArchetype); chunkBufferTypeHandle.m_TypeLookupCache = (short)typeIndexInArchetype; if (Hint.Unlikely(typeIndexInArchetype == -1)) { @@ -1932,8 +1972,8 @@ public readonly LowLevel.Unsafe.UnsafeUntypedBufferAccessor GetUntypedBufferAcce int internalCapacity = archetype->BufferCapacities[typeIndexInArchetype]; var typeInfo = TypeManager.GetTypeInfo(chunkBufferTypeHandle.m_TypeIndex); byte* ptr = (chunkBufferTypeHandle.IsReadOnly) - ? ChunkDataUtility.GetComponentDataRO(m_Chunk, 0, typeIndexInArchetype) - : ChunkDataUtility.GetComponentDataRW(m_Chunk, 0, typeIndexInArchetype, chunkBufferTypeHandle.GlobalSystemVersion); + ? ChunkDataUtility.GetComponentDataRO(m_Chunk, archetype, 0, typeIndexInArchetype) + : ChunkDataUtility.GetComponentDataRW(m_Chunk, archetype, 0, typeIndexInArchetype, chunkBufferTypeHandle.GlobalSystemVersion); var length = Count; int stride = archetype->SizeOfs[typeIndexInArchetype]; @@ -2015,13 +2055,7 @@ public struct ChunkHeader : ICleanupComponentData /// /// Constructs a ChunkHeader representing an empty chunk. /// - public static unsafe ChunkHeader Null - { - get - { - return new ChunkHeader {ArchetypeChunk = new ArchetypeChunk(null, null)}; - } - } + public static ChunkHeader Null => new(); } /// diff --git a/Unity.Entities/Iterators/BufferLookup.cs b/Unity.Entities/Iterators/BufferLookup.cs index 76531a7..0435f9e 100644 --- a/Unity.Entities/Iterators/BufferLookup.cs +++ b/Unity.Entities/Iterators/BufferLookup.cs @@ -178,12 +178,12 @@ public bool DidChange(Entity entity, uint version) { var ecs = m_Access->EntityComponentStore; var chunk = ecs->GetChunk(entity); - var archetype = chunk->Archetype; + var archetype = ecs->GetArchetype(chunk); if (Hint.Unlikely(archetype != m_Cache.Archetype)) m_Cache.Update(archetype, m_TypeIndex); var typeIndexInArchetype = m_Cache.IndexInArchetype; if (typeIndexInArchetype == -1) return false; - var chunkVersion = chunk->GetChangeVersion(typeIndexInArchetype); + var chunkVersion = archetype->Chunks.GetChangeVersion(typeIndexInArchetype, chunk.ListIndex); return ChangeVersionUtility.DidChange(chunkVersion, version); } diff --git a/Unity.Entities/Iterators/ChunkDataGatherJobs.cs b/Unity.Entities/Iterators/ChunkDataGatherJobs.cs index 3b66d92..4f7532a 100644 --- a/Unity.Entities/Iterators/ChunkDataGatherJobs.cs +++ b/Unity.Entities/Iterators/ChunkDataGatherJobs.cs @@ -28,7 +28,6 @@ public void Execute(int index) { var srcChunk = archetype->Chunks[i]; Chunks[offset + i] = new ArchetypeChunk(srcChunk, entityComponentStore); - } } } @@ -145,7 +144,7 @@ public void Execute() var matchingArchetypesPtr = MatchingArchetypes.Ptr; var requiresFilter = Filter.RequiresMatchesFilter; var hasEnableableComponents = QueryContainsEnableableComponents == 1; - var cachedChunksPtr = ChunkCache.Ptr; + var cachedChunksIndices = ChunkCache.ChunkIndices; var chunkMatchingArchetypeIndexPtr = ChunkCache.PerChunkMatchingArchetypeIndex->Ptr; var chunkIndexInArchetypePtr = ChunkCache.ChunkIndexInArchetype->Ptr; int cachedChunkCount = ChunkCache.Length; @@ -168,7 +167,7 @@ public void Execute() { for (int chunkIndexInCache = 0; chunkIndexInCache < cachedChunkCount; ++chunkIndexInCache) { - outChunks[chunkIndexInCache] = new ArchetypeChunk(cachedChunksPtr[chunkIndexInCache], ecs); + outChunks[chunkIndexInCache] = new ArchetypeChunk(cachedChunksIndices[chunkIndexInCache], ecs); } filteredChunkCount = cachedChunkCount; } @@ -194,7 +193,7 @@ public void Execute() out var chunkEnabledMask); if (chunkEnabledMask.ULong0 == 0 && chunkEnabledMask.ULong1 == 0) continue; - outChunks[filteredChunkCount++] = new ArchetypeChunk(cachedChunksPtr[chunkIndexInCache], ecs); + outChunks[filteredChunkCount++] = new ArchetypeChunk(cachedChunksIndices[chunkIndexInCache], ecs); } } else @@ -210,7 +209,7 @@ public void Execute() int chunkIndexInArchetype = chunkIndexInArchetypePtr[chunkIndexInCache]; if (!currentMatchingArchetype->ChunkMatchesFilter(chunkIndexInArchetype, ref Filter)) continue; - outChunks[filteredChunkCount++] = new ArchetypeChunk(cachedChunksPtr[chunkIndexInCache], ecs); + outChunks[filteredChunkCount++] = new ArchetypeChunk(cachedChunksIndices[chunkIndexInCache], ecs); } } *OutFilteredChunksList.Length = filteredChunkCount; @@ -306,7 +305,7 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE ushort typeSize = archetype->SizeOfs[indexInTypeArray]; int baseEntityIndexInQuery = ChunkBaseEntityIndices[unfilteredChunkIndex]; - byte* srcBytes = chunk.m_Chunk->Buffer + typeOffset; + byte* srcBytes = chunk.m_Chunk.Buffer + typeOffset; byte* dstBytes = ComponentData + (baseEntityIndexInQuery * typeSize); if (useEnabledMask) { @@ -341,7 +340,7 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE int baseEntityIndexInQuery = ChunkBaseEntityIndices[unfilteredChunkIndex]; byte* dstBytes = OutputList.Ptr + (baseEntityIndexInQuery * typeSize); - byte* srcBytes = ChunkDataUtility.GetComponentDataWithTypeRO(chunk.m_Chunk, 0, TypeHandle.m_TypeIndex); + byte* srcBytes = ChunkDataUtility.GetComponentDataWithTypeRO(chunk.m_Chunk, archetype, 0, TypeHandle.m_TypeIndex); int chunkEntityCount = chunk.Count; int copyCount = useEnabledMask ? EnabledBitUtility.countbits(chunkEnabledMask) : chunkEntityCount; #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG @@ -388,7 +387,7 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE ushort typeSize = archetype->SizeOfs[indexInTypeArray]; int baseEntityIndexInQuery = ChunkBaseEntityIndices[unfilteredChunkIndex]; - var dstBytes = ChunkDataUtility.GetComponentDataWithTypeRW(chunk.m_Chunk, 0, TypeIndex, GlobalSystemVersion); + var dstBytes = ChunkDataUtility.GetComponentDataWithTypeRW(chunk.m_Chunk, archetype, 0, TypeIndex, GlobalSystemVersion); var srcBytes = ComponentData + (baseEntityIndexInQuery * typeSize); if (useEnabledMask) { @@ -421,7 +420,7 @@ public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useE ushort typeSize = archetype->SizeOfs[indexInTypeArray]; int baseEntityIndexInQuery = ChunkBaseEntityIndices[unfilteredChunkIndex]; - byte* dstBytes = ChunkDataUtility.GetComponentDataWithTypeRW(chunk.m_Chunk, 0, TypeHandle.m_TypeIndex, TypeHandle.GlobalSystemVersion); + byte* dstBytes = ChunkDataUtility.GetComponentDataWithTypeRW(chunk.m_Chunk, archetype, 0, TypeHandle.m_TypeIndex, TypeHandle.GlobalSystemVersion); byte* srcBytes = InputList.Ptr + (baseEntityIndexInQuery * typeSize); int chunkEntityCount = chunk.Count; int copyCount = useEnabledMask ? EnabledBitUtility.countbits(chunkEnabledMask) : chunkEntityCount; diff --git a/Unity.Entities/Iterators/ChunkIterationUtility.cs b/Unity.Entities/Iterators/ChunkIterationUtility.cs index c80defe..590fae8 100644 --- a/Unity.Entities/Iterators/ChunkIterationUtility.cs +++ b/Unity.Entities/Iterators/ChunkIterationUtility.cs @@ -14,10 +14,10 @@ namespace Unity.Entities { /// - /// Enables iteration over chunks belonging to a set of archetypes. + /// Enables iteration over chunks belonging to a set of archetypes. /// [BurstCompile] - [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "!NET_DOTS")] + [GenerateTestsForBurstCompatibility()] internal unsafe partial struct ChunkIterationUtility { /// @@ -108,7 +108,7 @@ public static void ToArchetypeChunkList(in UnsafeCachedChunkList cachedChunkList in UnsafeMatchingArchetypePtrList matchingArchetypes, ref EntityQueryFilter filter, int hasEnableableComponents, ref NativeList outChunks) { - var cachedChunksPtr = cachedChunkList.Ptr; + var cachedChunkIndices = cachedChunkList.ChunkIndices; var matchingArchetypesPtr = matchingArchetypes.Ptr; var requiresFilter = filter.RequiresMatchesFilter; var hasEnableable = hasEnableableComponents != 0; @@ -125,7 +125,7 @@ public static void ToArchetypeChunkList(in UnsafeCachedChunkList cachedChunkList { for (int chunkIndexInCache = 0; chunkIndexInCache < cachedChunkCount; ++chunkIndexInCache) { - outChunks.AddNoResize(new ArchetypeChunk(cachedChunksPtr[chunkIndexInCache], ecs)); + outChunks.AddNoResize(new ArchetypeChunk(cachedChunkIndices[chunkIndexInCache], ecs)); } } else if (hasEnableable) @@ -149,7 +149,7 @@ public static void ToArchetypeChunkList(in UnsafeCachedChunkList cachedChunkList out var chunkEnabledMask); if (chunkEnabledMask.ULong0 == 0 && chunkEnabledMask.ULong1 == 0) continue; - outChunks.AddNoResize(new ArchetypeChunk(cachedChunksPtr[chunkIndexInCache], ecs)); + outChunks.AddNoResize(new ArchetypeChunk(cachedChunkIndices[chunkIndexInCache], ecs)); } } else @@ -165,7 +165,7 @@ public static void ToArchetypeChunkList(in UnsafeCachedChunkList cachedChunkList int chunkIndexInArchetype = chunkIndexInArchetypePtr[chunkIndexInCache]; if (!currentMatchingArchetype->ChunkMatchesFilter(chunkIndexInArchetype, ref filter)) continue; - outChunks.AddNoResize(new ArchetypeChunk(cachedChunksPtr[chunkIndexInCache], ecs)); + outChunks.AddNoResize(new ArchetypeChunk(cachedChunkIndices[chunkIndexInCache], ecs)); } } } @@ -180,7 +180,7 @@ public static void GatherEntitiesWithFilter(Entity* entities,ref EntityQueryFilt v128 chunkEnabledMask = default; while (chunkCacheIterator.MoveNextChunk(ref chunkIndex, out var chunk, out var chunkEntityCount, out byte useEnableBits, ref chunkEnabledMask)) { - Entity* chunkEntities = (Entity*)chunk.m_Chunk->Buffer; // Entity is always the first table in the chunk buffer + Entity* chunkEntities = (Entity*)chunk.m_Chunk.Buffer; // Entity is always the first table in the chunk buffer if (useEnableBits == 0) { UnsafeUtility.MemCpy(copyDest, chunkEntities, chunkEntityCount * sizeof(Entity)); @@ -212,7 +212,7 @@ public static void GatherEntitiesWithoutFilter(Entity* entities, in UnsafeCached while (chunkCacheIterator.MoveNextChunk(ref chunkIndex, out var chunk, out int chunkEntityCount, out byte useEnabledBits, ref chunkEnabledBits)) { - Entity* copySrc = (Entity*)chunk.m_Chunk->Buffer; // Entity is always the first table in the chunk buffer + Entity* copySrc = (Entity*)chunk.m_Chunk.Buffer; // Entity is always the first table in the chunk buffer UnsafeUtility.MemCpy(copyDest, copySrc, chunkEntityCount*sizeof(Entity)); copyDest += chunkEntityCount; } @@ -303,7 +303,7 @@ public static NativeArray CreateEntityArrayAsync(UnsafeMatchingArchetype /// Handle to the GatherEntitiesJob job used to fill the output array. /// Handle to a job this GatherEntitiesJob must wait on. /// NativeList of the entities in a given EntityQuery. - [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) }, RequiredUnityDefine = "!NET_DOTS")] + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public static NativeList CreateEntityListAsync(AllocatorManager.AllocatorHandle allocator, EntityTypeHandle typeHandle, EntityQuery entityQuery, @@ -350,7 +350,7 @@ public static void GatherComponentDataWithFilter(byte* componentData, TypeIndex var chunkArchetype = chunkCacheIterator._CurrentMatchingArchetype->Archetype; if (chunkArchetype != typeLookupCache.Archetype) typeLookupCache.Update(chunkArchetype, typeIndex); - var chunkComponentData = chunk.m_Chunk->Buffer + typeLookupCache.ComponentOffset; + var chunkComponentData = chunk.m_Chunk.Buffer + typeLookupCache.ComponentOffset; if (useEnableBits == 0) { @@ -387,7 +387,7 @@ public static void GatherComponentDataWithoutFilter(byte* componentData, TypeInd var chunkArchetype = chunkCacheIterator._CurrentMatchingArchetype->Archetype; if (chunkArchetype != typeLookupCache.Archetype) typeLookupCache.Update(chunkArchetype, typeIndex); - var copySrc = chunk.m_Chunk->Buffer + typeLookupCache.ComponentOffset; + var copySrc = chunk.m_Chunk.Buffer + typeLookupCache.ComponentOffset; var copySize = typeLookupCache.ComponentSizeOf * chunkEntityCount; UnsafeUtility.MemCpy(copyDest, copySrc, copySize); copyDest += copySize; @@ -448,7 +448,7 @@ public static NativeArray CreateComponentDataArrayAsync( /// Input job dependencies for the array-populating job. /// Handle to the job that will populate the output array. The caller must complete this job before the output array contents are valid. /// NativeList of all the chunks in the matchingArchetypes list. - [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) }, RequiredUnityDefine = "!NET_DOTS")] + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public static NativeList CreateComponentDataListAsync( AllocatorManager.AllocatorHandle allocator, ref DynamicComponentTypeHandle typeHandle, @@ -492,7 +492,7 @@ public static NativeList CreateComponentDataListAsync( /// Number of entities that match the query. Used as the output array size. /// Entities that match this query will be included in the output. /// NativeArray of all the chunks in the matchingArchetypes list. - [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) }, RequiredUnityDefine = "!NET_DOTS")] + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public static NativeArray CreateComponentDataArray( AllocatorManager.AllocatorHandle allocator, ref ComponentTypeHandle typeHandle, @@ -600,7 +600,7 @@ public static void CopyFromComponentDataArrayAsync(UnsafeMatchingArchetypePtr jobHandle = job.ScheduleParallel(entityQuery, baseIndexJob); } - [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) }, RequiredUnityDefine = "!NET_DOTS")] + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public static void CopyFromComponentDataListAsync( NativeList componentDataList, ref DynamicComponentTypeHandle typeHandle, @@ -630,7 +630,7 @@ out JobHandle outJobHandle outJobHandle = job.ScheduleParallelByRef(entityQuery, baseIndexJob); } - [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) }, RequiredUnityDefine = "!NET_DOTS")] + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public static void CopyFromComponentDataArray( NativeArray componentDataArray, ref ComponentTypeHandle typeHandle, @@ -971,21 +971,20 @@ public static int CalculateEntityCount(in UnsafeCachedChunkList cachedChunkList, #if ENABLE_UNITY_COLLECTIONS_CHECKS [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) }, RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS", CompileTarget = GenerateTestsForBurstCompatibilityAttribute.BurstCompatibleCompileTarget.Editor)] - internal static BufferAccessor GetChunkBufferAccessor(Chunk* chunk, bool isWriting, int typeIndexInArchetype, uint systemVersion, AtomicSafetyHandle safety0, AtomicSafetyHandle safety1) + internal static BufferAccessor GetChunkBufferAccessor(Archetype* archetype, ChunkIndex chunk, bool isWriting, int typeIndexInArchetype, uint systemVersion, AtomicSafetyHandle safety0, AtomicSafetyHandle safety1) #else [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] - internal static BufferAccessor GetChunkBufferAccessor(Chunk * chunk, bool isWriting, int typeIndexInArchetype, uint systemVersion) + internal static BufferAccessor GetChunkBufferAccessor(Archetype* archetype, ChunkIndex chunk, bool isWriting, int typeIndexInArchetype, uint systemVersion) #endif where T : unmanaged, IBufferElementData { - var archetype = chunk->Archetype; int internalCapacity = archetype->BufferCapacities[typeIndexInArchetype]; byte* ptr = (!isWriting) - ? ChunkDataUtility.GetComponentDataRO(chunk, 0, typeIndexInArchetype) - : ChunkDataUtility.GetComponentDataRW(chunk, 0, typeIndexInArchetype, systemVersion); + ? ChunkDataUtility.GetComponentDataRO(chunk, archetype, 0, typeIndexInArchetype) + : ChunkDataUtility.GetComponentDataRW(chunk, archetype, 0, typeIndexInArchetype, systemVersion); - var length = chunk->Count; + var length = chunk.Count; int stride = archetype->SizeOfs[typeIndexInArchetype]; #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING @@ -995,8 +994,8 @@ internal static BufferAccessor GetChunkBufferAccessor(Chunk * chunk, bool recordType: EntitiesJournaling.RecordType.GetBufferRW, entityComponentStore: archetype->EntityComponentStore, globalSystemVersion: systemVersion, - chunks: chunk, - chunkCount: 1, + archetype: archetype, + chunk: chunk, types: &archetype->Types[typeIndexInArchetype].TypeIndex, typeCount: 1); } @@ -1009,18 +1008,9 @@ internal static BufferAccessor GetChunkBufferAccessor(Chunk * chunk, bool #endif } - internal static void* GetChunkComponentDataPtr(Chunk* chunk, bool isWriting, int indexInArchetype, uint systemVersion) + internal static void* GetChunkComponentDataROPtr(Archetype* archetype, ChunkIndex chunk, int indexInArchetype) { - byte* ptr = (!isWriting) - ? ChunkDataUtility.GetComponentDataRO(chunk, 0, indexInArchetype) - : ChunkDataUtility.GetComponentDataRW(chunk, 0, indexInArchetype, systemVersion); - return ptr; - } - - internal static void* GetChunkComponentDataROPtr(Chunk* chunk, int indexInArchetype) - { - var archetype = chunk->Archetype; - return chunk->Buffer + archetype->Offsets[indexInArchetype]; + return chunk.Buffer + archetype->Offsets[indexInArchetype]; } // Helper struct to bundle some invariant data when computing the enabled-bit mask for several chunks within the same archetype @@ -1167,15 +1157,15 @@ internal static void GetEnabledMask(int chunkIndexInArchetype, int chunkEntityCo } [BurstCompile] - public static void GetEnabledMask(Chunk* chunk, MatchingArchetype* matchingArchetype, out v128 enabledMask) + public static void GetEnabledMask(ChunkIndex chunk, MatchingArchetype* matchingArchetype, out v128 enabledMask) { Assert.IsTrue(matchingArchetype->Archetype->ChunkCapacity <= 128); - GetEnabledMask(chunk->ListIndex, chunk->Count, new EnabledMaskMatchingArchetypeState(matchingArchetype), out enabledMask); + GetEnabledMask(chunk.ListIndex, chunk.Count, new EnabledMaskMatchingArchetypeState(matchingArchetype), out enabledMask); } - internal static void GetEnabledMaskNoBurstForTests(Chunk* chunk, MatchingArchetype* matchingArchetype, out v128 enabledMask) + internal static void GetEnabledMaskNoBurstForTests(ChunkIndex chunk, MatchingArchetype* matchingArchetype, out v128 enabledMask) { Assert.IsTrue(matchingArchetype->Archetype->ChunkCapacity <= 128); - GetEnabledMask(chunk->ListIndex, chunk->Count, new EnabledMaskMatchingArchetypeState(matchingArchetype), out enabledMask); + GetEnabledMask(chunk.ListIndex, chunk.Count, new EnabledMaskMatchingArchetypeState(matchingArchetype), out enabledMask); } @@ -1184,7 +1174,7 @@ public static void SetEnabledBitsOnAllChunks(ref EntityQueryImpl queryImpl, Type { var chunkList = queryImpl.GetMatchingChunkCache(); var chunkCount = chunkList.Length; - Chunk** chunkListPtr = chunkList.Ptr; + var chunkIndices = chunkList.ChunkIndices; int* chunkIndexInArchetypePtr = chunkList.ChunkIndexInArchetype->Ptr; MatchingArchetype** matchingArchetypesPtr = queryImpl._QueryData->MatchingArchetypes.Ptr; int* matchingArchetypeIndicesPtr = chunkList.PerChunkMatchingArchetypeIndex->Ptr; @@ -1198,7 +1188,7 @@ public static void SetEnabledBitsOnAllChunks(ref EntityQueryImpl queryImpl, Type int* chunkEntityCountsPtr = null; for (int chunkIndexInCache = 0; chunkIndexInCache < chunkCount; ++chunkIndexInCache) { - var chunk = chunkListPtr[chunkIndexInCache]; + var chunk = chunkIndices[chunkIndexInCache]; if (Hint.Unlikely(matchingArchetypeIndicesPtr[chunkIndexInCache] != currentMatchingArchetypeIndex)) { // Update per-archetype state @@ -1214,7 +1204,7 @@ public static void SetEnabledBitsOnAllChunks(ref EntityQueryImpl queryImpl, Type chunkEnabledBitsIncrement = currentArchetype->Chunks.GetComponentEnabledBitsSizePerChunk() / sizeof(v128); } - if (requiresFilter && !chunk->MatchesFilter(currentMatchingArchetype, ref queryImpl._Filter)) + if (requiresFilter && !chunk.MatchesFilter(currentMatchingArchetype, ref queryImpl._Filter)) continue; int chunkIndexInArchetype = chunkIndexInArchetypePtr[chunkIndexInCache]; @@ -1243,7 +1233,7 @@ public static void SetEnabledBitsOnAllChunks(ref EntityQueryImpl queryImpl, Type /// [GenerateTestsForBurstCompatibility] [BurstCompile] - public static class EnabledBitUtility + internal static class EnabledBitUtility { /// /// Retrieves the next contiguous range of set bits starting at or after the provided index in the provided mask. @@ -1256,7 +1246,7 @@ public static class EnabledBitUtility /// True if another range of contiguous bits was found (in which case, the range info is stored in /// and . Otherwise, false. [MethodImpl(MethodImplOptions.AggressiveInlining)] - public static bool TryGetNextRange(v128 mask, int firstIndexToCheck, out int nextRangeBegin, out int nextRangeEnd) + internal static bool TryGetNextRange(v128 mask, int firstIndexToCheck, out int nextRangeBegin, out int nextRangeEnd) { mask = ShiftRight(mask, firstIndexToCheck); diff --git a/Unity.Entities/Iterators/ComponentLookup.cs b/Unity.Entities/Iterators/ComponentLookup.cs index 4c400ae..0b2eb0a 100644 --- a/Unity.Entities/Iterators/ComponentLookup.cs +++ b/Unity.Entities/Iterators/ComponentLookup.cs @@ -200,12 +200,12 @@ public bool DidChange(Entity entity, uint version) { var ecs = m_Access->EntityComponentStore; var chunk = ecs->GetChunk(entity); - var archetype = chunk->Archetype; + var archetype = ecs->GetArchetype(chunk); if (Hint.Unlikely(archetype != m_Cache.Archetype)) m_Cache.Update(archetype, m_TypeIndex); var typeIndexInArchetype = m_Cache.IndexInArchetype; if (typeIndexInArchetype == -1) return false; - var chunkVersion = chunk->GetChangeVersion(typeIndexInArchetype); + var chunkVersion = archetype->Chunks.GetChangeVersion(typeIndexInArchetype, chunk.ListIndex); return ChangeVersionUtility.DidChange(chunkVersion, version); } diff --git a/Unity.Entities/Iterators/DynamicBuffer.cs b/Unity.Entities/Iterators/DynamicBuffer.cs index f1815ee..4db4f2e 100644 --- a/Unity.Entities/Iterators/DynamicBuffer.cs +++ b/Unity.Entities/Iterators/DynamicBuffer.cs @@ -641,13 +641,6 @@ public void CopyFrom(T[] v) if (v == null) throw new ArgumentNullException(nameof(v)); -#if NET_DOTS - Clear(); - foreach (var d in v) - { - Add(d); - } -#else ResizeUninitialized(v.Length); CheckWriteAccess(); @@ -656,13 +649,11 @@ public void CopyFrom(T[] v) UnsafeUtility.MemCpy(BufferHeader.GetElementPointer(m_Buffer), (void*)num, Length * UnsafeUtility.SizeOf()); gcHandle.Free(); -#endif } } internal sealed class DynamicBufferDebugView where T : unmanaged { -#if !NET_DOTS private DynamicBuffer _buffer; public DynamicBufferDebugView(DynamicBuffer source) { @@ -670,6 +661,5 @@ public DynamicBufferDebugView(DynamicBuffer source) } public T[] Items => _buffer.AsNativeArray().ToArray(); -#endif } } diff --git a/Unity.Entities/Iterators/EntityQuery.cs b/Unity.Entities/Iterators/EntityQuery.cs index 56f490d..99099e1 100644 --- a/Unity.Entities/Iterators/EntityQuery.cs +++ b/Unity.Entities/Iterators/EntityQuery.cs @@ -46,6 +46,7 @@ public EntityQueryDescValidationException(string message) : base(message) /// * None - Excludes archetypes that have any component in this set, but includes entities which have the component disabled. /// * Disabled - Includes archetypes that have every component in this set, but only matches entities where the component is disabled. /// * Absent - Excludes archetypes that have any component in this set. + /// * Present - Includes archetypes that have every component in this set, whether or not the component is enabled. /// /// For example, given entities with the following components: /// @@ -89,6 +90,10 @@ public class EntityQueryDesc : IEquatable /// public ComponentType[] Absent = Array.Empty(); /// + /// Include archetypes that contain these component types, whether or not the component is enabled. + /// + public ComponentType[] Present = Array.Empty(); + /// /// Specialized query options. /// /// @@ -119,7 +124,7 @@ void AddComponentTypeIndicesToArray(ComponentType[] componentTypes, ref NativeAr public void Validate() { // Determine the number of ComponentTypes contained in the filters - var itemCount = None.Length + All.Length + Any.Length + Disabled.Length + Absent.Length; + var itemCount = None.Length + All.Length + Any.Length + Disabled.Length + Absent.Length + Present.Length; // Project all the ComponentType Ids of None, All, Any queryDesc filters into the same array to identify duplicated later on @@ -130,6 +135,7 @@ public void Validate() AddComponentTypeIndicesToArray(Any, ref allComponentTypeIds, ref curComponentTypeIndex); AddComponentTypeIndicesToArray(Disabled, ref allComponentTypeIds, ref curComponentTypeIndex); AddComponentTypeIndicesToArray(Absent, ref allComponentTypeIds, ref curComponentTypeIndex); + AddComponentTypeIndicesToArray(Present, ref allComponentTypeIds, ref curComponentTypeIndex); // Check for duplicate, only if necessary if (itemCount > 1) @@ -144,14 +150,9 @@ public void Validate() var curId = allComponentTypeIds[i]; if (curId == refId) { -#if NET_DOTS - throw new EntityQueryDescValidationException( - $"The component type with index {curId} appears multiple times in an EntityQueryDesc. Duplicate component types are not allowed within an EntityQueryDesc."); -#else var compType = TypeManager.GetType(curId); throw new EntityQueryDescValidationException( $"The component type {compType.Name} appears multiple times in an EntityQueryDesc. Duplicate component types are not allowed within an EntityQueryDesc."); -#endif } refId = curId; @@ -194,6 +195,8 @@ public bool Equals(EntityQueryDesc other) return false; if (!ArraysEquivalent(Absent, other.Absent)) return false; + if (!ArraysEquivalent(Present, other.Present)) + return false; return true; } @@ -235,6 +238,7 @@ public override int GetHashCode() result = (result * 397) ^ (None ?? Array.Empty()).GetHashCode(); result = (result * 397) ^ (Disabled ?? Array.Empty()).GetHashCode(); result = (result * 397) ^ (Absent ?? Array.Empty()).GetHashCode(); + result = (result * 397) ^ (Present ?? Array.Empty()).GetHashCode(); return result; } @@ -292,6 +296,7 @@ public enum EntityQueryOptions /// - Entities with all required enableable components will be matched, even if the components are disabled. /// - Entities with any optional enableable components will be matched, even if the components are disabled. /// - Entities with any excluded enableable component will be matched, even if the components are enabled. + /// - Entities with any excluded non-enableable component will NOT be matched; their archetype is not in the potentially matching set. /// - Entities missing a required component will NOT be matched; their archetype is not in the potentially matching set. /// - Entities missing all the optional components will NOT be matched; their archetype is not in the potentially matching set. /// @@ -377,7 +382,7 @@ public bool Matches(Entity entity) /// would not. public bool MatchesIgnoreFilter(ArchetypeChunk chunk) { - return chunk.m_Chunk->Archetype->CompareMask(this); + return chunk.Archetype.Archetype->CompareMask(this); } /// Obsolete. Use instead. /// The chunk to check. @@ -511,6 +516,15 @@ internal ComponentType[] GetQueryTypes() { types.Add(ComponentType.Exclude(TypeManager.GetType(_QueryData->ArchetypeQueries[i].Absent[j]))); } + for (var j = 0; j < _QueryData->ArchetypeQueries[i].PresentCount; ++j) + { + var accessMode = (ComponentType.AccessMode)_QueryData->ArchetypeQueries[i].PresentAccessMode[j]; + var type = TypeManager.GetType(_QueryData->ArchetypeQueries[i].Present[j]); + + types.Add(accessMode == ComponentType.AccessMode.ReadOnly + ? ComponentType.ReadOnly(type) + : ComponentType.ReadWrite(type)); + } } using (var typesArray = types.ToNativeArray(Allocator.Temp)) @@ -1120,7 +1134,7 @@ public NativeArray ToComponentDataArray(AllocatorManager.AllocatorHandle a var chunkArchetype = chunkCacheIterator._CurrentMatchingArchetype->Archetype; if (chunkArchetype != typeLookupCache.Archetype) typeLookupCache.Update(chunkArchetype, typeIndex); - var chunkManagedComponentArray = (int*)ChunkDataUtility.GetComponentDataRO(chunk.m_Chunk, 0, typeLookupCache.IndexInArchetype); + var chunkManagedComponentArray = (int*)ChunkDataUtility.GetComponentDataRO(chunk.m_Chunk, chunkArchetype, 0, typeLookupCache.IndexInArchetype); if (useEnableBits == 0) { for (int entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex) @@ -1276,29 +1290,30 @@ public Entity GetSingletonEntity() throw new InvalidOperationException($"Can't call GetSingletonEntity() on queries containing enableable component types."); #endif GetSingletonChunk(TypeManager.GetTypeIndex(), out var indexInArchetype, out var chunk); - return UnsafeUtility.AsRef(ChunkIterationUtility.GetChunkComponentDataROPtr(chunk, 0)); + var archetype = _Access->EntityComponentStore->GetArchetype(chunk); + return UnsafeUtility.AsRef(ChunkIterationUtility.GetChunkComponentDataROPtr(archetype, chunk, 0)); } [MethodImpl(MethodImplOptions.NoInlining)] - internal void GetSingletonChunk(TypeIndex typeIndex, out int outIndexInArchetype, out Chunk* outChunk) + internal void GetSingletonChunk(TypeIndex typeIndex, out int outIndexInArchetype, out ChunkIndex outChunk) { if (!_Filter.RequiresMatchesFilter && _QueryData->RequiredComponentsCount <= 2 && _QueryData->RequiredComponents[1].TypeIndex == typeIndex) { // Fast path with no filtering var matchingChunkCache = GetMatchingChunkCache(); #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - if (matchingChunkCache.Length != 1 || matchingChunkCache.Ptr[0]->Count != 1) + if (matchingChunkCache.Length != 1 || matchingChunkCache.ChunkIndices[0].Count != 1) { _QueryData->CheckChunkListCacheConsistency(false); var typeName = typeIndex.ToFixedString(); - if (matchingChunkCache.Length == 0 || matchingChunkCache.Ptr[0]->Count == 0) + if (matchingChunkCache.Length == 0 || matchingChunkCache.ChunkIndices[0].Count == 0) throw new InvalidOperationException($"GetSingleton<{typeName}>() requires that exactly one entity exists that match this query, but there are none. Are you missing a call to RequireForUpdate()? You could also use TryGetSingleton()"); else throw new InvalidOperationException(@$"GetSingleton<{typeName}>() requires that exactly one entity exists that match this query, but there are {CalculateEntityCountWithoutFiltering()} entities in {matchingChunkCache.Length} chunks. -First chunk: entityCount={matchingChunkCache.Ptr[0]->Count}, archetype={matchingChunkCache.Ptr[0]->Archetype->ToString()}."); +First chunk: entityCount={matchingChunkCache.ChunkIndices[0].Count}, archetype={_Access->EntityComponentStore->GetArchetype(matchingChunkCache.ChunkIndices[0])->ToString()}."); } #endif - outChunk = matchingChunkCache.Ptr[0]; // only one matching chunk + outChunk = matchingChunkCache.ChunkIndices[0]; // only one matching chunk var matchIndex = matchingChunkCache.PerChunkMatchingArchetypeIndex->Ptr[0]; var match = _QueryData->MatchingArchetypes.Ptr[matchIndex]; outIndexInArchetype = match->IndexInArchetype[1]; @@ -1328,7 +1343,7 @@ internal void GetSingletonChunk(TypeIndex typeIndex, out int outIndexInArchetype var chunk = chunkList[i]; var matchIndex = matchingArchetypeIndices[i]; var match = matchingArchetypes[matchIndex]; - if (match->ChunkMatchesFilter(chunk->ListIndex, ref _Filter)) + if (match->ChunkMatchesFilter(chunk.ListIndex, ref _Filter)) { outIndexInArchetype = match->IndexInArchetype[indexInQuery]; outChunk = chunk; @@ -1359,7 +1374,8 @@ public RefRW GetSingletonRW() where T : unmanaged, IComponentData GetSingletonChunk(typeIndex, out var indexInArchetype, out var chunk); - var data = ChunkDataUtility.GetComponentDataRW(chunk, 0, indexInArchetype, _Access->EntityComponentStore->GlobalSystemVersion); + var archetype = _Access->EntityComponentStore->GetArchetype(chunk); + var data = ChunkDataUtility.GetComponentDataRW(chunk, archetype, 0, indexInArchetype, _Access->EntityComponentStore->GlobalSystemVersion); #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING if (Hint.Unlikely(_Access->EntityComponentStore->m_RecordToJournal != 0)) @@ -1376,14 +1392,14 @@ public RefRW GetSingletonRW() where T : unmanaged, IComponentData #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING [MethodImpl(MethodImplOptions.NoInlining)] - internal void RecordSingletonJournalRW(Chunk* chunk, TypeIndex typeIndex, EntitiesJournaling.RecordType type, void* data = null, int size = 0) + internal void RecordSingletonJournalRW(ChunkIndex chunk, TypeIndex typeIndex, EntitiesJournaling.RecordType type, void* data = null, int size = 0) { EntitiesJournaling.AddRecord( recordType: type, worldSequenceNumber: _Access->m_WorldUnmanaged.SequenceNumber, executingSystem: _Access->m_WorldUnmanaged.ExecutingSystem, - chunks: chunk, - chunkCount: 1, + archetype: _Access->EntityComponentStore->GetArchetype(chunk), + chunk: chunk, types: &typeIndex, typeCount: 1, data: data, @@ -1420,26 +1436,28 @@ public T GetSingleton() where T : unmanaged, IComponentData { var matchingChunkCache = GetMatchingChunkCache(); #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - if (matchingChunkCache.Length != 1 || matchingChunkCache.Ptr[0]->Count != 1) + if (matchingChunkCache.Length != 1 || matchingChunkCache.ChunkIndices[0].Count != 1) { _QueryData->CheckChunkListCacheConsistency(false); var typeName = typeIndex.ToFixedString(); - if (matchingChunkCache.Length == 0 || matchingChunkCache.Ptr[0]->Count == 0) + if (matchingChunkCache.Length == 0 || matchingChunkCache.ChunkIndices[0].Count == 0) throw new InvalidOperationException($"GetSingleton<{typeName}>() requires that exactly one entity exists that match this query, but there are none. Are you missing a call to RequireForUpdate()? You could also use TryGetSingleton()"); else throw new InvalidOperationException(@$"GetSingleton<{typeName}>() requires that exactly one entity exists that match this query, but there are {CalculateEntityCountWithoutFiltering()} entities in {matchingChunkCache.Length} chunks. -First chunk: entityCount={matchingChunkCache.Ptr[0]->Count}, archetype={matchingChunkCache.Ptr[0]->Archetype->ToString()}."); +First chunk: entityCount={matchingChunkCache.ChunkIndices[0].Count}, archetype={_Access->EntityComponentStore->GetArchetype(matchingChunkCache.ChunkIndices[0])->ToString()}."); } #endif - var chunk = matchingChunkCache.Ptr[0]; // only one matching chunk + var chunk = matchingChunkCache.ChunkIndices[0]; // only one matching chunk var matchIndex = matchingChunkCache.PerChunkMatchingArchetypeIndex->Ptr[0]; var match = _QueryData->MatchingArchetypes.Ptr[matchIndex]; - return UnsafeUtility.AsRef(ChunkIterationUtility.GetChunkComponentDataROPtr(chunk, match->IndexInArchetype[1])); + var archetype = _Access->EntityComponentStore->GetArchetype(chunk); + return UnsafeUtility.AsRef(ChunkIterationUtility.GetChunkComponentDataROPtr(archetype, chunk, match->IndexInArchetype[1])); } else { GetSingletonChunk(typeIndex, out var indexInArchetype, out var chunk); - return UnsafeUtility.AsRef(ChunkIterationUtility.GetChunkComponentDataROPtr(chunk, indexInArchetype)); + var archetype = _Access->EntityComponentStore->GetArchetype(chunk); + return UnsafeUtility.AsRef(ChunkIterationUtility.GetChunkComponentDataROPtr(archetype, chunk, indexInArchetype)); } } @@ -1465,13 +1483,14 @@ public DynamicBuffer GetSingletonBuffer(bool isReadOnly = false) where T : if (Hint.Unlikely(_Access->EntityComponentStore->m_RecordToJournal != 0) && !isReadOnly) RecordSingletonJournalRW(chunk, typeIndex, EntitiesJournaling.RecordType.GetBufferRW); #endif + var archetype = _Access->EntityComponentStore->GetArchetype(chunk); #if ENABLE_UNITY_COLLECTIONS_CHECKS var safetyHandles = &_Access->DependencyManager->Safety; - var bufferAccessor = ChunkIterationUtility.GetChunkBufferAccessor(chunk, !isReadOnly, indexInArchetype, + var bufferAccessor = ChunkIterationUtility.GetChunkBufferAccessor(archetype, chunk, !isReadOnly, indexInArchetype, _Access->EntityComponentStore->GlobalSystemVersion, safetyHandles->GetSafetyHandle(typeIndex, isReadOnly), safetyHandles->GetBufferSafetyHandle(typeIndex)); #else - var bufferAccessor = ChunkIterationUtility.GetChunkBufferAccessor(chunk, !isReadOnly, indexInArchetype, + var bufferAccessor = ChunkIterationUtility.GetChunkBufferAccessor(archetype, chunk, !isReadOnly, indexInArchetype, _Access->EntityComponentStore->GlobalSystemVersion); #endif return bufferAccessor[0]; @@ -1510,8 +1529,16 @@ public bool HasSingleton() $"Can't call HasSingleton<{typeName}>() with enableable component type {typeName}."); } #endif - - return CalculateEntityCount() == 1; + int matchingEntityCount = CalculateEntityCount(); +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(matchingEntityCount > 1)) + { + var typeName = typeIndex.ToFixedString(); + throw new InvalidOperationException( + $"HasSingleton<{typeName}>() found {matchingEntityCount} instances of {typeName}; there must only be either zero or one."); + } +#endif + return matchingEntityCount == 1; } public bool TryGetSingletonBuffer(out DynamicBuffer value, bool isReadOnly = false) @@ -1781,13 +1808,13 @@ public bool Debugger_GetData(List entities, List chunks) { var chunk = archetype->Chunks[c]; - if (!chunk->MatchesFilter(matchingArchetype, ref _Filter)) + if (!chunk.MatchesFilter(matchingArchetype, ref _Filter)) continue; - var chunkEntities = (Entity*)ChunkDataUtility.GetComponentDataRO(chunk, 0, 0); + var chunkEntities = (Entity*)ChunkDataUtility.GetComponentDataRO(chunk, archetype, 0, 0); ChunkIterationUtility.GetEnabledMask(chunk, matchingArchetype, out var enabledMask); - int entityCount = chunk->Count; + int entityCount = chunk.Count; if (entityCount > TypeManager.MaximumChunkCapacity) return false; if (EnabledBitUtility.countbits(enabledMask) == 0) @@ -1837,14 +1864,15 @@ public bool Matches(Entity e) if (hasFilter || hasEnableableComponents) { var chunk = ecs->GetChunk(e); + var archetype = ecs->GetArchetype(chunk); // TODO(DOTS-6802): most of this work could be amortized, if we knew Matches() was being called on entities in the same chunk. var matchingArchetype = _QueryData->MatchingArchetypes.Ptr[ - EntityQueryManager.FindMatchingArchetypeIndexForArchetype(ref _QueryData->MatchingArchetypes, chunk->Archetype)]; + EntityQueryManager.FindMatchingArchetypeIndexForArchetype(ref _QueryData->MatchingArchetypes, archetype)]; // Is the chunk filtered out? if (hasFilter) { SyncChangeFilterTypes(); - if (!chunk->MatchesFilter(matchingArchetype, ref _Filter)) + if (!chunk.MatchesFilter(matchingArchetype, ref _Filter)) return false; } // Does the entity have all required components enabled? @@ -1935,6 +1963,16 @@ public EntityQueryDesc GetEntityQueryDesc() }; } + var presentComponentTypes = new ComponentType[archetypeQuery->PresentCount]; + for (var i = 0; i < archetypeQuery->PresentCount; ++i) + { + presentComponentTypes[i] = new ComponentType + { + TypeIndex = archetypeQuery->Present[i], + AccessModeType = (ComponentType.AccessMode)archetypeQuery->PresentAccessMode[i] + }; + } + return new EntityQueryDesc { All = allComponentTypes, @@ -1942,6 +1980,7 @@ public EntityQueryDesc GetEntityQueryDesc() None = noneComponentTypes, Disabled = disabledComponentTypes, Absent = absentComponentTypes, + Present = presentComponentTypes, Options = archetypeQuery->Options }; } @@ -2005,9 +2044,12 @@ internal static void Free(EntityQueryImpl* impl) /// A query description 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 - /// * `None` = None of the component types in this array can exist in the archetype + /// * `All` = All component types in this array must exist in the archetype, and must be enabled on matching entities. + /// * `Any` = At least one of the component types in this array must exist in the archetype, and must be enabled on matching entities. + /// * `None` = None of the component types in this array can exist in the archetype, or they must be present and disabled on matching entities. + /// * `Disabled` = All component types in this array must exist in the archetype, and must be disabled on matching entities. + /// * `Absent` = None of the component types in this array can exist in the archetype + /// * `Present` = All of the component types in this array must exist in the archetype, whether or not they are enabled. /// /// For example, the following query includes archetypes containing Rotation and /// RotationSpeed components, but excludes any archetypes containing a Static component: @@ -2683,7 +2725,7 @@ public void CopyFromComponentDataListAsync(NativeList componentDataList, J /// A singleton component is a component of which only one instance exists that satisfies this query. /// The component type. /// A copy of the singleton component. - /// + /// Thrown if the number of entities that match this query is not exactly one. /// /// /// @@ -2699,10 +2741,10 @@ public T GetSingleton() where T : unmanaged, IComponentData /// A singleton component is a component of which only one instance exists that satisfies this query. /// The component type. /// A copy of the singleton component. - /// + /// Thrown if the number of entities that match this query is not exactly one. /// /// - /// + /// /// [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public RefRW GetSingletonRW() where T : unmanaged, IComponentData @@ -2716,6 +2758,7 @@ public RefRW GetSingletonRW() where T : unmanaged, IComponentData /// This component type must not implement /// The component. if an with the specified type does not exist in the , this is assigned a default value /// True, if exactly one exists in the with the provided component type. + /// Thrown if the number of entities that match this query is greater than one. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public bool TryGetSingleton(out T value) where T : unmanaged, IComponentData @@ -2729,7 +2772,7 @@ public bool TryGetSingleton(out T value) /// The component type. /// The reference of the component /// A reference to the singleton component. - /// + /// Thrown if the number of entities that match this query is greater than one. /// /// /// @@ -2746,6 +2789,7 @@ public bool TryGetSingletonRW(out RefRW value) where T : unmanaged, ICompo /// The subtype of the singleton component. /// This component type must not implement /// True, if a singleton is found to match exactly once with the specified type. + /// Thrown if the number of entities that match this query is greater than one. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public bool HasSingleton() => _GetImpl()->HasSingleton(); @@ -2759,6 +2803,7 @@ public bool HasSingleton() /// The buffer. if an with the specified type does not exist in the , this is assigned a default value /// Whether the buffer data is read-only or not. Set to false by default. /// True, if exactly one matches the with the provided component type. + /// Thrown if the number of entities that match this query is greater than one. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] public bool TryGetSingletonBuffer(out DynamicBuffer value, bool isReadOnly = false) where T : unmanaged, IBufferElementData @@ -2772,6 +2817,7 @@ public bool TryGetSingletonBuffer(out DynamicBuffer value, bool isReadOnly /// The associated with the specified singleton component. /// If a singleton of the specified types does not exist in the current , this is set to Entity.Null /// True, if exactly one matches the with the provided component type. + /// Thrown if the number of entities that match this query is greater than one. [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] public bool TryGetSingletonEntity(out Entity value) => _GetImpl()->TryGetSingletonEntity(out value); @@ -2786,7 +2832,7 @@ public bool TryGetSingletonEntity(out Entity value) /// The buffer element type. /// If the caller does not need to modify the buffer contents, pass true here. /// The singleton buffer. - /// + /// Thrown if the number of entities that match this query is not exactly one. /// /// [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleBufferElement) })] @@ -3169,6 +3215,12 @@ public void AddSharedComponentFilter(SharedComponent sharedComp return __impl; } + internal EntityComponentStore* GetEntityComponentStore() + { + _CheckSafetyHandle(); + return __impl->_Access->EntityComponentStore; + } + [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] private void _CheckSafetyHandle() { @@ -3242,7 +3294,8 @@ public static T GetSingleton(this EntityQuery query) where T : class impl->GetSingletonChunk(typeIndex, out var indexInArchetype, out var chunk); - int managedComponentIndex = *(int*)ChunkDataUtility.GetComponentDataRO(chunk, 0, indexInArchetype); + var archetype = query.GetEntityComponentStore()->GetArchetype(chunk); + int managedComponentIndex = *(int*)ChunkDataUtility.GetComponentDataRO(chunk, archetype, 0, indexInArchetype); return (T)access->ManagedComponentStore.GetManagedComponent(managedComponentIndex); } @@ -3298,8 +3351,9 @@ public static T GetSingletonRW(this EntityQuery query) where T : class #endif impl->GetSingletonChunk(typeIndex, out var indexInArchetype, out var chunk); + var archetype = access->EntityComponentStore->GetArchetype(chunk); - int managedComponentIndex = *(int*)ChunkDataUtility.GetComponentDataRW(chunk, 0, indexInArchetype, access->EntityComponentStore->GlobalSystemVersion); + int managedComponentIndex = *(int*)ChunkDataUtility.GetComponentDataRW(chunk, archetype, 0, indexInArchetype, access->EntityComponentStore->GlobalSystemVersion); #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING var store = access->EntityComponentStore; @@ -3309,8 +3363,8 @@ public static T GetSingletonRW(this EntityQuery query) where T : class recordType: EntitiesJournaling.RecordType.GetComponentObjectRW, worldSequenceNumber: access->m_WorldUnmanaged.SequenceNumber, executingSystem: access->m_WorldUnmanaged.ExecutingSystem, - chunks: chunk, - chunkCount: 1, + archetype: access->EntityComponentStore->GetArchetype(chunk), + chunk: chunk, types: &typeIndex, typeCount: 1); } @@ -3385,7 +3439,9 @@ public static void SetSingleton(this EntityQuery query, T value) where T : cl var store = access->EntityComponentStore; impl->GetSingletonChunk(typeIndex, out var indexInArchetype, out var chunk); - managedComponentIndex = (int*)ChunkDataUtility.GetComponentDataRW(chunk, 0, indexInArchetype, store->GlobalSystemVersion); + var archetype = store->GetArchetype(chunk); + + managedComponentIndex = (int*)ChunkDataUtility.GetComponentDataRW(chunk, archetype, 0, indexInArchetype, store->GlobalSystemVersion); #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING if (Hint.Unlikely(store->m_RecordToJournal != 0)) diff --git a/Unity.Entities/Iterators/EntityQueryBuilder.cs b/Unity.Entities/Iterators/EntityQueryBuilder.cs index 7396536..0069939 100644 --- a/Unity.Entities/Iterators/EntityQueryBuilder.cs +++ b/Unity.Entities/Iterators/EntityQueryBuilder.cs @@ -19,11 +19,12 @@ namespace Unity.Entities /// A query description combines the component types you specify (using methods like `WithAll`, `WithAny`, and `WithNone`) /// sets according to the following rules: /// - /// * All - Includes archetypes that have every component in this set + /// * All - Includes archetypes that have every component in this set, but only includes entities where the component is enabled. /// * Any - Includes archetypes that have at least one component in this set /// * None - Excludes archetypes that have any component in this set, but includes entities which have the component disabled. /// * Disabled - Includes archetypes that have every component in this set, but only matches entities where the component is disabled. /// * Absent - Excludes archetypes that have any component in this set. + /// * Present - Includes archetypes that have every component in this set, whether or not the components are enabled. /// /// For example, given entities with the following components: /// @@ -55,6 +56,7 @@ internal struct QueryTypes public ComponentIndexArray None; public ComponentIndexArray Disabled; public ComponentIndexArray Absent; + public ComponentIndexArray Present; public EntityQueryOptions Options; } @@ -78,6 +80,7 @@ internal struct BuilderData internal UnsafeList _none; internal UnsafeList _disabled; internal UnsafeList _absent; + internal UnsafeList _present; internal EntityQueryOptions _pendingOptions; internal byte _isFinalized; } @@ -105,6 +108,7 @@ public EntityQueryBuilder(AllocatorManager.AllocatorHandle allocator) _builderDataPtr->_none = new UnsafeList(6, _allocator); _builderDataPtr->_disabled = new UnsafeList(6, _allocator); _builderDataPtr->_absent = new UnsafeList(6, _allocator); + _builderDataPtr->_present = new UnsafeList(6, _allocator); _builderDataPtr->_pendingOptions = default; _builderDataPtr->_isFinalized = 0; @@ -132,6 +136,7 @@ internal EntityQueryBuilder(AllocatorManager.AllocatorHandle allocator, Componen _builderDataPtr->_none = new UnsafeList(6, _allocator); _builderDataPtr->_disabled = new UnsafeList(6, _allocator); _builderDataPtr->_absent = new UnsafeList(6, _allocator); + _builderDataPtr->_present = new UnsafeList(6, _allocator); _builderDataPtr->_pendingOptions = default; _builderDataPtr->_isFinalized = 0; @@ -224,7 +229,10 @@ public EntityQueryBuilder AddAll(ComponentType t) /// Add required component types to the query. /// /// - /// To match the resulting query, an Entity must have all of the query's required component types. + /// To match the resulting query, an Entity must have all of the query's required component types, and any required + /// types that implement must be enabled. + /// To require components to be disabled on matching entities, use . + /// To require components regardless of whether they are enabled on matching entities, use . /// /// WithAll accepts up to seven type arguments. You can add more component types by chaining calls together. /// @@ -1003,6 +1011,8 @@ internal EntityQueryBuilder WithNone(ComponentType* componentTypes, int count) /// /// To match the resulting query, an Entity must have all of the query's required component types, *and* they /// must all be disabled. + /// To require components to be enabled on matching entities, use . + /// To require components regardless of whether they are enabled on matching entities, use . /// /// There are several ways to exclude components from a query: /// - WithAbsent<T>() matches all entities in chunks that do not have T at all. @@ -1443,6 +1453,266 @@ internal EntityQueryBuilder WithAbsent(ComponentType* componentTypes, int count) } + /// + /// Add required component types to the query, whether the type is enabled or not. + /// + /// + /// To match the resulting query, an Entity must have all of the query's required component types, whether they + /// are enabled or not. + /// To match required types only if they're enabled, use . + /// To match required types only if they're disabled, use . + /// + /// WithPresent accepts up to seven type arguments. You can add more component types by chaining calls together. + /// + /// + /// + /// + /// + /// To add component types that are not known at compile time, use + /// + /// The query will request read-only access to the referenced component(s). To request read/write access, use + /// + /// A required component type + /// The builder object that invoked this method. + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] + public EntityQueryBuilder WithPresent() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_isFinalized = 0; + return this; + } + + /// + /// A required component type + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData) })] + public EntityQueryBuilder WithPresent() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_isFinalized = 0; + return this; + } + /// + /// A required component type + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData), + typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData) + })] + public EntityQueryBuilder WithPresent() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_isFinalized = 0; + return this; + } + /// + /// A required component type + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData), + typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData) + })] + public EntityQueryBuilder WithPresent() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_isFinalized = 0; + return this; + } + /// + /// A required component type + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData), + typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData), + typeof(BurstCompatibleComponentData) + })] + public EntityQueryBuilder WithPresent() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_isFinalized = 0; + return this; + } + /// + /// A required component type + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData), + typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData), + typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData) + })] + public EntityQueryBuilder WithPresent() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_isFinalized = 0; + return this; + } + /// + /// A required component type + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData), + typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData), + typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData) + })] + public EntityQueryBuilder WithPresent() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadOnly }); + _builderDataPtr->_isFinalized = 0; + return this; + } + + /// + /// Add required component types to the query with ReadWrite mode, whether they are enabled or disabled. + /// + /// + /// If a query uses the option, + /// you must use WithPresentRW to specify the query's writeable required components. Refer to the + /// [write groups guide](xref:systems-write-groups) for more information. + /// + /// To match the resulting query, an Entity must have all of the query's required component types, regardless of + /// whether they are enabled or disabled. + /// To match required types only if they're enabled, use . + /// To match required types only if they're disabled, use . + /// + /// WithPresentRW accepts up to two type arguments. You can add more component types by chaining calls together. + /// + /// + /// + /// + /// + /// To request read-only access to the reference component(s), use + /// + /// A required ReadWrite component type + /// The builder object that invoked this method. + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] + public EntityQueryBuilder WithPresentRW() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadWrite }); + _builderDataPtr->_isFinalized = 0; + return this; + } + /// + /// A required ReadWrite component type + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData), typeof(BurstCompatibleComponentData) })] + public EntityQueryBuilder WithPresentRW() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadWrite }); + _builderDataPtr->_present.Add(new ComponentType{ TypeIndex = TypeManager.GetTypeIndex(), AccessModeType = ComponentType.AccessMode.ReadWrite }); + _builderDataPtr->_isFinalized = 0; + return this; + } + + /// + /// Add a required [Chunk Component](xref:components-chunk) type to the query, whether it is enabled or disabled. + /// + /// + /// Call this method on the query builder to find entities that have all the specified chunk components, whether it + /// is enabled or disabled. Chunk components are a distinct component type, which are different from adding the + /// same type as a standard component. + /// + /// + /// + /// + /// + /// To add additional required Chunk Components, call this method multiple times. + /// + /// The query will request read-only access to the referenced component(s). To request read/write access, use + /// + /// Component type to use as a required, read-only Chunk Component + /// The builder object that invoked this method. + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] + public EntityQueryBuilder WithPresentChunkComponent() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(ComponentType.ChunkComponentReadOnly()); + _builderDataPtr->_isFinalized = 0; + return this; + } + + /// + /// Component type to use as a required, read-write Chunk Component + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(BurstCompatibleComponentData) })] + public EntityQueryBuilder WithPresentChunkComponentRW() + { + CheckBuilderPtr(); + + _builderDataPtr->_present.Add(ComponentType.ChunkComponent()); + _builderDataPtr->_isFinalized = 0; + return this; + } + + /// + /// Add a list of required component types to the query, whether they are enabled or disabled. + /// + /// + /// To match the resulting query, an Entity must have all of the query's required component types, regardless of + /// whether they are enabled or disabled. + /// + /// + /// A list of component types that implements . + /// For example, or + /// + /// + /// A container of component types + /// The builder object that invoked this method. + [GenerateTestsForBurstCompatibility(GenericTypeArguments = new[] { typeof(FixedList32Bytes)})] + public EntityQueryBuilder WithPresent(ref T componentTypes) + where T : INativeList + { + CheckBuilderPtr(); + + for (var i = 0; i < componentTypes.Length; i++) + { + _builderDataPtr->_present.Add(componentTypes[i]); + } + + _builderDataPtr->_isFinalized = 0; + return this; + } + + internal EntityQueryBuilder WithPresent(ComponentType* componentTypes, int count) + { + CheckBuilderPtr(); + + for (var i = 0; i < count; i++) + { + _builderDataPtr->_present.Add(componentTypes[i]); + } + + _builderDataPtr->_isFinalized = 0; + return this; + } + /// /// Add an additional query description to a single EntityQuery. /// @@ -1499,6 +1769,7 @@ internal EntityQueryBuilder FinalizeQueryInternal() TransferArray(ref _builderDataPtr->_none, ref qd.None); TransferArray(ref _builderDataPtr->_disabled, ref qd.Disabled); TransferArray(ref _builderDataPtr->_absent, ref qd.Absent); + TransferArray(ref _builderDataPtr->_present, ref qd.Present); qd.Options = _builderDataPtr->_pendingOptions; // Add the QueryTypes struct to the list of _indexData. There should be one QueryTypes @@ -1534,6 +1805,7 @@ public void Dispose() _builderDataPtr->_none.Dispose(); _builderDataPtr->_disabled.Dispose(); _builderDataPtr->_absent.Dispose(); + _builderDataPtr->_present.Dispose(); if (CollectionHelper.ShouldDeallocate(_allocator)) { @@ -1562,6 +1834,7 @@ public void Reset() _builderDataPtr->_none.Clear(); _builderDataPtr->_disabled.Clear(); _builderDataPtr->_absent.Clear(); + _builderDataPtr->_present.Clear(); _builderDataPtr->_pendingOptions = default; _builderDataPtr->_isFinalized = 0; } @@ -1626,7 +1899,6 @@ internal static void ValidateComponentTypes(in UnsafeList compone } } -#if !NET_DOTS [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] [BurstDiscard] internal static void ThrowDuplicateComponentTypeError(TypeIndex curId) @@ -1635,14 +1907,13 @@ internal static void ThrowDuplicateComponentTypeError(TypeIndex curId) throw new EntityQueryDescValidationException( $"EntityQuery contains a filter with duplicate component type name {typeName}. Queries can only contain a single component of a given type in a filter."); } -#endif [Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS"), Conditional("UNITY_DOTS_DEBUG")] internal static void Validate(in UnsafeList allTypes, in UnsafeList anyTypes, in UnsafeList noneTypes, - in UnsafeList disabledTypes, in UnsafeList absentTypes) + in UnsafeList disabledTypes, in UnsafeList absentTypes, in UnsafeList presentTypes) { // Determine the number of ComponentTypes contained in the filters - var itemCount = allTypes.Length + anyTypes.Length + noneTypes.Length + disabledTypes.Length + absentTypes.Length; + var itemCount = allTypes.Length + anyTypes.Length + noneTypes.Length + disabledTypes.Length + absentTypes.Length + presentTypes.Length; // Project all the ComponentType Ids of None, All, Any queryDesc filters into the same array to identify duplicated later on @@ -1652,6 +1923,7 @@ internal static void Validate(in UnsafeList allTypes, in UnsafeLi ValidateComponentTypes(noneTypes, ref allComponentTypeIds); ValidateComponentTypes(disabledTypes, ref allComponentTypeIds); ValidateComponentTypes(absentTypes, ref allComponentTypeIds); + ValidateComponentTypes(presentTypes, ref allComponentTypeIds); // Check for duplicate, only if necessary if (itemCount > 1) @@ -1666,9 +1938,7 @@ internal static void Validate(in UnsafeList allTypes, in UnsafeLi var curId = allComponentTypeIds[i]; if (curId == refId) { -#if !NET_DOTS ThrowDuplicateComponentTypeError(curId); -#endif throw new EntityQueryDescValidationException( $"EntityQuery contains an EntityQueryDesc with duplicate component type index {curId}. Queries can only contain a single component of a given type in a EntityQueryDesc."); } diff --git a/Unity.Entities/Iterators/EntityQueryManager.cs b/Unity.Entities/Iterators/EntityQueryManager.cs index fcccbb3..dee44e2 100644 --- a/Unity.Entities/Iterators/EntityQueryManager.cs +++ b/Unity.Entities/Iterators/EntityQueryManager.cs @@ -2,6 +2,7 @@ using System.Diagnostics; using System.Runtime.InteropServices; using Unity.Burst; +using Unity.Burst.CompilerServices; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Mathematics; @@ -19,8 +20,6 @@ internal struct EntityQuerySafetyHandles // 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). internal AtomicSafetyHandle m_Safety0; - // TODO(DOTS-6573): Enable this path in DOTSRT once it supports AtomicSafetyHandle.SetExclusiveWeak() -#if !UNITY_DOTSRUNTIME // Enableable components from query used to schedule job. To add more handles here, you must also increase // EntityQueryManager.MAX_ENABLEABLE_COMPONENTS_PER_QUERY. internal AtomicSafetyHandle m_SafetyEnableable1; @@ -31,7 +30,6 @@ internal struct EntityQuerySafetyHandles internal AtomicSafetyHandle m_SafetyEnableable6; internal AtomicSafetyHandle m_SafetyEnableable7; internal AtomicSafetyHandle m_SafetyEnableable8; -#endif internal int m_SafetyReadOnlyCount; internal int m_SafetyReadWriteCount; @@ -40,11 +38,6 @@ internal unsafe EntityQuerySafetyHandles(EntityQueryImpl* queryImpl) this = default; // workaround for CS0171 error (all fields must be fully assigned before control is returned) var queryData = queryImpl->_QueryData; m_Safety0 = queryImpl->SafetyHandles->GetEntityManagerSafetyHandle(); -#if UNITY_DOTSRUNTIME - // TODO(DOTS-6573): DOTSRT can use the main code path once it supports AtomicSafetyHandle.SetExclusiveWeak() - m_SafetyReadOnlyCount = 1; // for EntityManager handle - m_SafetyReadWriteCount = 0; -#else m_SafetyReadOnlyCount = 1 + queryData->EnableableComponentTypeIndexCount; // +1 for EntityManager handle m_SafetyReadWriteCount = 0; fixed (AtomicSafetyHandle* pEnableableHandles = &m_SafetyEnableable1) @@ -60,7 +53,6 @@ internal unsafe EntityQuerySafetyHandles(EntityQueryImpl* queryImpl) } } } -#endif } } #endif @@ -223,6 +215,7 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi var noneTypes = new UnsafeList(32, Allocator.Temp); var disabledTypes = new UnsafeList(32, Allocator.Temp); var absentTypes = new UnsafeList(32, Allocator.Temp); + var presentTypes = new UnsafeList(32, Allocator.Temp); #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG var entityTypeIndex = TypeManager.GetTypeIndex(); #endif @@ -234,7 +227,7 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi for (int i = typesAll.Index; i < typesAll.Index + typesAll.Count; i++) { #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG - if (types[i].TypeIndex == entityTypeIndex) + if (Hint.Unlikely(types[i].TypeIndex == entityTypeIndex)) { throw new ArgumentException("Entity is not allowed in list of component types for EntityQuery"); } @@ -247,6 +240,12 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi var typesAny = queryData[q].Any; for (int i = typesAny.Index; i < typesAny.Index + typesAny.Count; i++) { +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(types[i].TypeIndex == entityTypeIndex)) + { + throw new ArgumentException("Entity is not allowed in list of component types for EntityQuery"); + } +#endif anyTypes.Add(types[i]); } } @@ -256,6 +255,10 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi for (int i = typesNone.Index; i < typesNone.Index + typesNone.Count; i++) { #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(types[i].TypeIndex == entityTypeIndex)) + { + throw new ArgumentException("Entity is not allowed in list of component types for EntityQuery"); + } var type = types[i]; // Can not use Assert.AreEqual here because it uses the (object, object) overload which // boxes the enums being compared, and that can not be burst compiled. @@ -269,6 +272,12 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi var typesDisabled = queryData[q].Disabled; for (int i = typesDisabled.Index; i < typesDisabled.Index + typesDisabled.Count; i++) { +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(types[i].TypeIndex == entityTypeIndex)) + { + throw new ArgumentException("Entity is not allowed in list of component types for EntityQuery"); + } +#endif disabledTypes.Add(types[i]); } } @@ -278,6 +287,10 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi for (int i = typesAbsent.Index; i < typesAbsent.Index + typesAbsent.Count; i++) { #if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(types[i].TypeIndex == entityTypeIndex)) + { + throw new ArgumentException("Entity is not allowed in list of component types for EntityQuery"); + } var type = types[i]; // Can not use Assert.AreEqual here because it uses the (object, object) overload which // boxes the enums being compared, and that can not be burst compiled. @@ -285,26 +298,41 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi #endif absentTypes.Add(types[i]); } + + { + var typesPresent = queryData[q].Present; + for (int i = typesPresent.Index; i < typesPresent.Index + typesPresent.Count; i++) + { +#if ENABLE_UNITY_COLLECTIONS_CHECKS || UNITY_DOTS_DEBUG + if (Hint.Unlikely(types[i].TypeIndex == entityTypeIndex)) + { + throw new ArgumentException("Entity is not allowed in list of component types for EntityQuery"); + } +#endif + presentTypes.Add(types[i]); + } + } } // Validate the queryBuilder has components declared in a consistent way - EntityQueryBuilder.Validate(allTypes, anyTypes, noneTypes, disabledTypes, absentTypes); + EntityQueryBuilder.Validate(allTypes, anyTypes, noneTypes, disabledTypes, absentTypes, presentTypes); var isFilterWriteGroup = (queryData[q].Options & EntityQueryOptions.FilterWriteGroup) != 0; if (isFilterWriteGroup) { // Each ReadOnly in any or all // if has WriteGroup types, - // - Recursively add to any (if not explictly mentioned) + // - Recursively add to any (if not explicitly mentioned) var explicitList = new UnsafeList(allTypes.Length + anyTypes.Length + noneTypes.Length + disabledTypes.Length + - absentTypes.Length + 16, Allocator.Temp); + absentTypes.Length + presentTypes.Length + 16, Allocator.Temp); explicitList.AddRange(allTypes); explicitList.AddRange(anyTypes); explicitList.AddRange(noneTypes); explicitList.AddRange(disabledTypes); explicitList.AddRange(absentTypes); + explicitList.AddRange(presentTypes); for (int i = 0; i < anyTypes.Length; i++) IncludeDependentWriteGroups(anyTypes[i], ref explicitList); @@ -312,6 +340,8 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi IncludeDependentWriteGroups(allTypes[i], ref explicitList); for (int i = 0; i < disabledTypes.Length; i++) IncludeDependentWriteGroups(disabledTypes[i], ref explicitList); + for (int i = 0; i < presentTypes.Length; i++) + IncludeDependentWriteGroups(presentTypes[i], ref explicitList); // Each ReadWrite in any or all // if has WriteGroup types, @@ -323,6 +353,8 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi ExcludeWriteGroups(allTypes[i], ref noneTypes, explicitList); for (int i = 0; i < disabledTypes.Length; i++) ExcludeWriteGroups(disabledTypes[i], ref noneTypes, explicitList); + for (int i = 0; i < presentTypes.Length; i++) + ExcludeWriteGroups(presentTypes[i], ref noneTypes, explicitList); explicitList.Dispose(); } @@ -336,12 +368,15 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi out outQuery[q].DisabledAccessMode, out outQuery[q].DisabledCount); ConstructTypeArray(ref unsafeScratchAllocator, absentTypes, out outQuery[q].Absent, out outQuery[q].AbsentAccessMode, out outQuery[q].AbsentCount); + ConstructTypeArray(ref unsafeScratchAllocator, presentTypes, out outQuery[q].Present, + out outQuery[q].PresentAccessMode, out outQuery[q].PresentCount); allTypes.Clear(); anyTypes.Clear(); noneTypes.Clear(); disabledTypes.Clear(); absentTypes.Clear(); + presentTypes.Clear(); outQuery[q].Options = queryData[q].Options; } @@ -350,6 +385,7 @@ void ExcludeWriteGroups(ComponentType type, ref UnsafeList noneLi noneTypes.Dispose(); disabledTypes.Dispose(); absentTypes.Dispose(); + presentTypes.Dispose(); return outQuery; } @@ -399,6 +435,8 @@ public static bool CompareQuery(in EntityQueryBuilder queryBuilder, EntityQueryD return false; if (!CompareQueryArray(queryBuilder, q.Absent, archetypeQuery.Absent, archetypeQuery.AbsentAccessMode, archetypeQuery.AbsentCount)) return false; + if (!CompareQueryArray(queryBuilder, q.Present, archetypeQuery.Present, archetypeQuery.PresentAccessMode, archetypeQuery.PresentCount)) + return false; if (q.Options != archetypeQuery.Options) return false; } @@ -433,8 +471,8 @@ private int IntersectSortedComponentIndexArrays(ComponentType* arrayA, int array // Calculates the intersection of "All" and "Disabled" arrays from the provided ArchetypeQuery objects private ComponentType* CalculateRequiredComponentsFromQuery(ref UnsafeScratchAllocator allocator, ArchetypeQuery* queries, int queryCount, out int outRequiredComponentsCount) { - // Populate and sort a combined array of all+disabled component types and their access modes from the first ArchetypeQuery - var maxIntersectionCount = queries[0].AllCount + queries[0].DisabledCount; + // Populate and sort a combined array of required component types and their access modes from the first ArchetypeQuery + var maxIntersectionCount = queries[0].AllCount + queries[0].DisabledCount + queries[0].PresentCount; // The first required component is always Entity. var outRequiredComponents = (ComponentType*)allocator.Allocate(maxIntersectionCount+1); outRequiredComponents[0] = ComponentType.ReadWrite(); @@ -455,6 +493,14 @@ private int IntersectSortedComponentIndexArrays(ComponentType* arrayA, int array AccessModeType = (ComponentType.AccessMode)queries[0].DisabledAccessMode[j], }; } + for (int j = 0; j < queries[0].PresentCount; ++j) + { + intersectionComponents[j+queries[0].AllCount+queries[0].DisabledCount] = new ComponentType + { + TypeIndex = queries[0].Present[j], + AccessModeType = (ComponentType.AccessMode)queries[0].PresentAccessMode[j], + }; + } NativeSortExtension.Sort(intersectionComponents, maxIntersectionCount); // For each additional ArchetypeQuery, create the same sorted array of component types, and reduce the @@ -463,7 +509,7 @@ private int IntersectSortedComponentIndexArrays(ComponentType* arrayA, int array var queryRequiredTypes = (ComponentType*)allocator.Allocate(maxIntersectionCount); for (int i = 1; i < queryCount; ++i) { - int queryRequiredCount = queries[i].AllCount + queries[i].DisabledCount; + int queryRequiredCount = queries[i].AllCount + queries[i].DisabledCount + queries[i].PresentCount; for (int j = 0; j < queries[i].AllCount; ++j) { queryRequiredTypes[j] = new ComponentType @@ -480,6 +526,14 @@ private int IntersectSortedComponentIndexArrays(ComponentType* arrayA, int array AccessModeType = (ComponentType.AccessMode)queries[i].DisabledAccessMode[j], }; } + for (int j = 0; j < queries[i].PresentCount; ++j) + { + queryRequiredTypes[j+queries[i].AllCount+queries[i].DisabledCount] = new ComponentType + { + TypeIndex = queries[i].Present[j], + AccessModeType = (ComponentType.AccessMode)queries[i].PresentAccessMode[j], + }; + } NativeSortExtension.Sort(queryRequiredTypes, queryRequiredCount); intersectionCount = IntersectSortedComponentIndexArrays(intersectionComponents, intersectionCount, queryRequiredTypes, queryRequiredCount, intersectionComponents); @@ -514,6 +568,10 @@ internal static void ConvertToEntityQueryBuilder(ref EntityQueryBuilder builder, { builder.WithAbsent(absentTypes, desc.Absent.Length); } + fixed (ComponentType* presentTypes = desc.Present) + { + builder.WithPresent(presentTypes, desc.Present.Length); + } builder.WithOptions(desc.Options); builder.FinalizeQueryInternal(); @@ -546,17 +604,17 @@ public EntityQuery CreateEntityQuery(EntityDataAccess* access, ComponentType* in return CreateEntityQuery(access, archetypeQueries, 1, outRequiredComponents, outRequiredComponentsCount); } - bool Matches(EntityQueryData* grp, ArchetypeQuery* archetypeQueries, int archetypeQueryCount, + bool Matches(EntityQueryData* queryData, ArchetypeQuery* archetypeQueries, int archetypeQueryCount, ComponentType* requiredComponents, int requiredComponentsCount) { - if (requiredComponentsCount != grp->RequiredComponentsCount) + if (requiredComponentsCount != queryData->RequiredComponentsCount) return false; - if (archetypeQueryCount != grp->ArchetypeQueryCount) + if (archetypeQueryCount != queryData->ArchetypeQueryCount) return false; - if (requiredComponentsCount > 0 && UnsafeUtility.MemCmp(requiredComponents, grp->RequiredComponents, sizeof(ComponentType) * requiredComponentsCount) != 0) + if (requiredComponentsCount > 0 && UnsafeUtility.MemCmp(requiredComponents, queryData->RequiredComponents, sizeof(ComponentType) * requiredComponentsCount) != 0) return false; for (var i = 0; i < archetypeQueryCount; ++i) - if (!archetypeQueries[i].Equals(grp->ArchetypeQueries[i])) + if (!archetypeQueries[i].Equals(queryData->ArchetypeQueries[i])) return false; return true; } @@ -619,7 +677,7 @@ public EntityQuery CreateEntityQuery(EntityDataAccess* access, { totalComponentCount += archetypeQueries[iAQ].AllCount + archetypeQueries[iAQ].AnyCount + archetypeQueries[iAQ].NoneCount + archetypeQueries[iAQ].DisabledCount + - archetypeQueries[iAQ].AbsentCount; + archetypeQueries[iAQ].AbsentCount + archetypeQueries[iAQ].PresentCount; } var allEnableableTypeIndices = new NativeList(totalComponentCount, Allocator.Temp); @@ -652,6 +710,8 @@ public EntityQuery CreateEntityQuery(EntityDataAccess* access, } // absent components are never present in a matching archetype, so it doesn't matter if they're // enableable or not. + // present components are always present in a matching archetype, so it doesn't matter if they're + // enableable or not. } // eliminate duplicate type indices if (allEnableableTypeIndices.Length > 0) @@ -689,11 +749,13 @@ public EntityQuery CreateEntityQuery(EntityDataAccess* access, queryData->ArchetypeQueries[i].None = (TypeIndex*)ChunkAllocate(queryData->ArchetypeQueries[i].NoneCount, archetypeQueries[i].None); queryData->ArchetypeQueries[i].Disabled = (TypeIndex*)ChunkAllocate(queryData->ArchetypeQueries[i].DisabledCount, archetypeQueries[i].Disabled); queryData->ArchetypeQueries[i].Absent = (TypeIndex*)ChunkAllocate(queryData->ArchetypeQueries[i].AbsentCount, archetypeQueries[i].Absent); + queryData->ArchetypeQueries[i].Present = (TypeIndex*)ChunkAllocate(queryData->ArchetypeQueries[i].PresentCount, archetypeQueries[i].Present); queryData->ArchetypeQueries[i].AllAccessMode = (byte*)ChunkAllocate(queryData->ArchetypeQueries[i].AllCount, archetypeQueries[i].AllAccessMode); queryData->ArchetypeQueries[i].AnyAccessMode = (byte*)ChunkAllocate(queryData->ArchetypeQueries[i].AnyCount, archetypeQueries[i].AnyAccessMode); queryData->ArchetypeQueries[i].NoneAccessMode = (byte*)ChunkAllocate(queryData->ArchetypeQueries[i].NoneCount, archetypeQueries[i].NoneAccessMode); queryData->ArchetypeQueries[i].DisabledAccessMode = (byte*)ChunkAllocate(queryData->ArchetypeQueries[i].DisabledCount, archetypeQueries[i].DisabledAccessMode); queryData->ArchetypeQueries[i].AbsentAccessMode = (byte*)ChunkAllocate(queryData->ArchetypeQueries[i].AbsentCount, archetypeQueries[i].AbsentAccessMode); + queryData->ArchetypeQueries[i].PresentAccessMode = (byte*)ChunkAllocate(queryData->ArchetypeQueries[i].PresentCount, archetypeQueries[i].PresentAccessMode); } var ecs = access->EntityComponentStore; @@ -717,13 +779,13 @@ public EntityQuery CreateEntityQuery(EntityDataAccess* access, return EntityQuery.Construct(queryData, access); } - void InitializeReaderWriter(EntityQueryData* grp, ComponentType* requiredTypes, int requiredCount) + void InitializeReaderWriter(EntityQueryData* queryData, ComponentType* requiredTypes, int requiredCount) { Assert.IsTrue(requiredCount > 0); Assert.IsTrue(requiredTypes[0] == ComponentType.ReadWrite()); - grp->ReaderTypesCount = 0; - grp->WriterTypesCount = 0; + queryData->ReaderTypesCount = 0; + queryData->WriterTypesCount = 0; for (var i = 1; i != requiredCount; i++) { @@ -733,16 +795,16 @@ void InitializeReaderWriter(EntityQueryData* grp, ComponentType* requiredTypes, switch (requiredTypes[i].AccessModeType) { case ComponentType.AccessMode.ReadOnly: - grp->ReaderTypesCount++; + queryData->ReaderTypesCount++; break; default: - grp->WriterTypesCount++; + queryData->WriterTypesCount++; break; } } - grp->ReaderTypes = (TypeIndex*)m_EntityQueryDataChunkAllocator.Allocate(sizeof(TypeIndex) * grp->ReaderTypesCount, 4); - grp->WriterTypes = (TypeIndex*)m_EntityQueryDataChunkAllocator.Allocate(sizeof(TypeIndex) * grp->WriterTypesCount, 4); + queryData->ReaderTypes = (TypeIndex*)m_EntityQueryDataChunkAllocator.Allocate(sizeof(TypeIndex) * queryData->ReaderTypesCount, 4); + queryData->WriterTypes = (TypeIndex*)m_EntityQueryDataChunkAllocator.Allocate(sizeof(TypeIndex) * queryData->WriterTypesCount, 4); var curReader = 0; var curWriter = 0; @@ -754,10 +816,10 @@ void InitializeReaderWriter(EntityQueryData* grp, ComponentType* requiredTypes, switch (requiredTypes[i].AccessModeType) { case ComponentType.AccessMode.ReadOnly: - grp->ReaderTypes[curReader++] = requiredTypes[i].TypeIndex; + queryData->ReaderTypes[curReader++] = requiredTypes[i].TypeIndex; break; default: - grp->WriterTypes[curWriter++] = requiredTypes[i].TypeIndex; + queryData->WriterTypes[curWriter++] = requiredTypes[i].TypeIndex; break; } } @@ -844,7 +906,7 @@ void AddArchetypeIfMatching(Archetype* archetype, EntityQueryData* query) if (TypeManager.IsEnableable(typeIndex)) { var currentTypeComponentIndex = ChunkDataUtility.GetNextIndexInTypeArray(archetype, typeIndex, typeComponentIndex); - // The archetype may not contain all the Any types (by definition; this is the whole point of Any). + // The archetype might not contain *all* the Any types (by definition; this is the whole point of Any). // Skip storing the missing types. if (currentTypeComponentIndex != -1) { @@ -866,6 +928,9 @@ void AddArchetypeIfMatching(Archetype* archetype, EntityQueryData* query) match->EnableableTypeMemoryOrderInArchetype_Disabled[enableableDisabledCount++] = archetype->TypeIndexInArchetypeToMemoryOrderIndex[typeComponentIndex]; } } + + // Absent types aren't handled because they're not present in the archetype at all, by definition. + // Present types aren't handled because we don't care if they're enabled or not, by definition. } static bool IsMatchingArchetype(Archetype* archetype, EntityQueryData* query) @@ -888,7 +953,8 @@ static bool IsMatchingArchetype(Archetype* archetype, ArchetypeQuery* query) && TestMatchingArchetypeAny(archetype, query->Any, query->AnyCount) // TODO: can we reuse existing methods for the two new arrays? (None for Absent, and All for Disabled)? && TestMatchingArchetypeAbsent(archetype, query->Absent, query->AbsentCount) - && TestMatchingArchetypeDisabled(archetype, query->Disabled, query->DisabledCount); + && TestMatchingArchetypeDisabled(archetype, query->Disabled, query->DisabledCount) + && TestMatchingArchetypePresent(archetype, query->Present, query->PresentCount); } static bool TestMatchingArchetypeAny(Archetype* archetype, TypeIndex* anyTypes, int anyCount) @@ -974,6 +1040,27 @@ static bool TestMatchingArchetypeDisabled(Archetype* archetype, TypeIndex* disab return presentButDisabledTypeCount == disabledCount; } + static bool TestMatchingArchetypePresent(Archetype* archetype, TypeIndex* presentTypes, int presentCount) + { + var types = archetype->Types; + var typeCount = archetype->TypesCount; + + int presentTypeCount = 0; + + for (var i = 0; i < typeCount; i++) + { + var typeIndex = types[i].TypeIndex; + + for (var j = 0; j < presentCount; j++) + { + var presentComponentTypeIndex = presentTypes[j]; + if (presentComponentTypeIndex == typeIndex) + presentTypeCount++; + } + } + return presentTypeCount == presentCount; + } + static bool TestMatchingArchetypeAll(Archetype* archetype, TypeIndex* allTypes, int allCount, EntityQueryOptions options) { var componentTypes = archetype->Types; @@ -1077,6 +1164,7 @@ unsafe struct MatchingArchetype public int EnableableComponentsCount_Any; public int EnableableComponentsCount_Disabled; // TODO(DOTS-7809): the None and Disabled lists can be combined here, as they're treated identically in all cases. // No need to count enableable absent components, since they're not in the archetype (by definition) + // No need to count enableable present components, since we don't care if they're enabled or not (by definition) public fixed int IndexInArchetype[1]; @@ -1251,6 +1339,10 @@ unsafe struct ArchetypeQuery : IEquatable public byte* AbsentAccessMode; public int AbsentCount; + public TypeIndex* Present; + public byte* PresentAccessMode; + public int PresentCount; + public EntityQueryOptions Options; public bool Equals(ArchetypeQuery other) @@ -1265,6 +1357,8 @@ public bool Equals(ArchetypeQuery other) return false; if (AbsentCount != other.AbsentCount) return false; + if (PresentCount != other.PresentCount) + return false; if (AnyCount > 0 && UnsafeUtility.MemCmp(Any, other.Any, sizeof(int) * AnyCount) != 0 && UnsafeUtility.MemCmp(AnyAccessMode, other.AnyAccessMode, sizeof(byte) * AnyCount) != 0) return false; @@ -1280,6 +1374,9 @@ public bool Equals(ArchetypeQuery other) if (AbsentCount > 0 && UnsafeUtility.MemCmp(Absent, other.Absent, sizeof(int) * AbsentCount) != 0 && UnsafeUtility.MemCmp(AbsentAccessMode, other.AbsentAccessMode, sizeof(byte) * AbsentCount) != 0) return false; + if (PresentCount > 0 && UnsafeUtility.MemCmp(Present, other.Present, sizeof(int) * PresentCount) != 0 && + UnsafeUtility.MemCmp(PresentAccessMode, other.PresentAccessMode, sizeof(byte) * PresentCount) != 0) + return false; if (Options != other.Options) return false; @@ -1295,17 +1392,20 @@ public override int GetHashCode() hashCode = 397 * hashCode ^ (NoneCount + 1); hashCode = 397 * hashCode ^ (DisabledCount + 1); hashCode = 397 * hashCode ^ (AbsentCount + 1); + hashCode = 397 * hashCode ^ (PresentCount + 1); hashCode = 397 * hashCode ^ (int)Options; hashCode = (int)math.hash(Any, sizeof(int) * AnyCount, (uint)hashCode); hashCode = (int)math.hash(All, sizeof(int) * AllCount, (uint)hashCode); hashCode = (int)math.hash(None, sizeof(int) * NoneCount, (uint)hashCode); hashCode = (int)math.hash(Disabled, sizeof(int) * DisabledCount, (uint)hashCode); hashCode = (int)math.hash(Absent, sizeof(int) * AbsentCount, (uint)hashCode); + hashCode = (int)math.hash(Present, sizeof(int) * PresentCount, (uint)hashCode); hashCode = (int)math.hash(AnyAccessMode, sizeof(byte) * AnyCount, (uint)hashCode); hashCode = (int)math.hash(AllAccessMode, sizeof(byte) * AllCount, (uint)hashCode); hashCode = (int)math.hash(NoneAccessMode, sizeof(byte) * NoneCount, (uint)hashCode); hashCode = (int)math.hash(DisabledAccessMode, sizeof(byte) * DisabledCount, (uint)hashCode); hashCode = (int)math.hash(AbsentAccessMode, sizeof(byte) * AbsentCount, (uint)hashCode); + hashCode = (int)math.hash(PresentAccessMode, sizeof(byte) * PresentCount, (uint)hashCode); return hashCode; } } @@ -1316,7 +1416,7 @@ public override int GetHashCode() unsafe struct UnsafeCachedChunkList { [NoAlias,NativeDisableUnsafePtrRestriction] - internal UnsafePtrList* MatchingChunks; + internal UnsafeList* MatchingChunks; [NoAlias,NativeDisableUnsafePtrRestriction] internal UnsafeList* PerChunkMatchingArchetypeIndex; @@ -1329,22 +1429,22 @@ unsafe struct UnsafeCachedChunkList internal int CacheValid; // must not be a bool, for Burst compatibility - internal Chunk** Ptr { get => (Chunk**)MatchingChunks->Ptr; } + internal ChunkIndex* ChunkIndices { get => MatchingChunks->Ptr; } public int Length { get => MatchingChunks->Length; } public bool IsCacheValid { get => CacheValid != 0; } internal UnsafeCachedChunkList(EntityComponentStore* entityComponentStore) { EntityComponentStore = entityComponentStore; - MatchingChunks = UnsafePtrList.Create(0, Allocator.Persistent); + MatchingChunks = UnsafeList.Create(0, Allocator.Persistent); PerChunkMatchingArchetypeIndex = UnsafeList.Create(0, Allocator.Persistent); ChunkIndexInArchetype = UnsafeList.Create(0, Allocator.Persistent); CacheValid = 0; } - internal void Append(Chunk** t, int addChunkCount, int matchingArchetypeIndex) + internal void Append(ChunkIndex* t, int addChunkCount, int matchingArchetypeIndex) { - MatchingChunks->AddRange(new UnsafePtrList(t, addChunkCount)); + MatchingChunks->AddRange(new UnsafeList(t, addChunkCount)); for (int i = 0; i < addChunkCount; ++i) { PerChunkMatchingArchetypeIndex->Add(matchingArchetypeIndex); @@ -1355,7 +1455,7 @@ internal void Append(Chunk** t, int addChunkCount, int matchingArchetypeIndex) internal void Dispose() { if (MatchingChunks != null) - UnsafePtrList.Destroy(MatchingChunks); + UnsafeList.Destroy(MatchingChunks); if (PerChunkMatchingArchetypeIndex != null) UnsafeList.Destroy(PerChunkMatchingArchetypeIndex); if (ChunkIndexInArchetype != null) @@ -1454,22 +1554,22 @@ internal static void AssertIsConsistent(in UnsafeCachedChunkList cache, in Entit Archetype has {archetypeChunkCount} chunks and {archetype->EntityCount} entities, with types {archetype->ToString()}. Query matches {archetypeCount} archetypes."); } - if (chunk->SequenceNumber != archetype->Chunks[chunkIndex]->SequenceNumber) + if (chunk.SequenceNumber != archetype->Chunks[chunkIndex].SequenceNumber) { // Chunk* comparisons are not sufficient, because chunk allocations are pooled. The chunk may have // been freed and reallocated into a new archetype. // This would indicate that some operation is not invalidating the appropriate query caches. - throw new ArgumentException(@$"query chunk cache inconsistency: cached chunk at index {chunkCounter} has a different sequence number {chunk->SequenceNumber} than chunk in matching archetype with sequence number {archetype->Chunks[chunkIndex]->SequenceNumber}. + throw new ArgumentException(@$"query chunk cache inconsistency: cached chunk at index {chunkCounter} has a different sequence number {chunk.SequenceNumber} than chunk in matching archetype with sequence number {archetype->Chunks[chunkIndex].SequenceNumber}. Archetype has {archetypeChunkCount} chunks and {archetype->EntityCount} entities, with types {archetype->ToString()}. Query matches {archetypeCount} archetypes."); } - if (cache.ChunkIndexInArchetype->Ptr[chunkCounter] != chunk->ListIndex) + if (cache.ChunkIndexInArchetype->Ptr[chunkCounter] != chunk.ListIndex) { // This chunk in the cache corresponds to the correct chunk in the archetype, but // its entry in ChunkIndexInArchetype is incorrect. // This would indicate that the cache's ChunkIndexInArchetype list is not being updated correctly when the // cache is updated. - throw new ArgumentException(@$"query chunk cache inconsistency: cached chunk at index {chunkCounter} thinks it should be at index {cache.ChunkIndexInArchetype->Ptr[chunkCounter]} in archetype {archetypeIndex}'s chunk list, but it's actually at index {chunk->ListIndex}. + throw new ArgumentException(@$"query chunk cache inconsistency: cached chunk at index {chunkCounter} thinks it should be at index {cache.ChunkIndexInArchetype->Ptr[chunkCounter]} in archetype {archetypeIndex}'s chunk list, but it's actually at index {chunk.ListIndex}. Archetype has {archetypeChunkCount} chunks and {archetype->EntityCount} entities, with types {archetype->ToString()}. Query matches {archetypeCount} archetypes."); } @@ -1517,12 +1617,12 @@ internal static void AssertIsConsistent(in UnsafeCachedChunkList cache, in Entit Archetype has {archetype->Chunks.Count} chunks and {archetype->EntityCount} entities, with types {archetype->ToString()}. Query matches {archetypeCount} archetypes."); } - if (archetype->Chunks[expectedChunkIndexInArchetype]->SequenceNumber != chunk->SequenceNumber) + if (archetype->Chunks[expectedChunkIndexInArchetype].SequenceNumber != chunk.SequenceNumber) { // Chunk* comparisons are not sufficient, because chunk allocations are pooled. The chunk may have // been freed and reallocated into a new archetype. // This would indicate that some operation is not invalidating the appropriate query caches. - throw new ArgumentException(@$"query chunk cache inconsistency: cached chunk {cacheChunkIndex} has a different sequence number {chunk->SequenceNumber} than chunk in matching archetype with sequence number {archetype->Chunks[expectedChunkIndexInArchetype]->SequenceNumber}. + throw new ArgumentException(@$"query chunk cache inconsistency: cached chunk {cacheChunkIndex} has a different sequence number {chunk.SequenceNumber} than chunk in matching archetype with sequence number {archetype->Chunks[expectedChunkIndexInArchetype].SequenceNumber}. Archetype has {archetype->Chunks.Count} chunks and {archetype->EntityCount} entities, with types {archetype->ToString()}. Query matches {archetypeCount} archetypes."); } diff --git a/Unity.Entities/Iterators/UnsafeChunkCacheIterator.cs b/Unity.Entities/Iterators/UnsafeChunkCacheIterator.cs index 739d7ce..ff23d59 100644 --- a/Unity.Entities/Iterators/UnsafeChunkCacheIterator.cs +++ b/Unity.Entities/Iterators/UnsafeChunkCacheIterator.cs @@ -16,7 +16,7 @@ unsafe struct UnsafeChunkCacheIterator [NoAlias] [NativeDisableUnsafePtrRestriction] - Chunk** _Chunks; + ChunkIndex* _Chunks; [NativeDisableUnsafePtrRestriction] [NoAlias] EntityComponentStore* _EntityComponentStore; @@ -38,7 +38,7 @@ unsafe struct UnsafeChunkCacheIterator internal UnsafeChunkCacheIterator(in EntityQueryFilter filter, bool hasEnableableComponents, UnsafeCachedChunkList list, MatchingArchetype** matchingArchetypes) { - _Chunks = list.Ptr; + _Chunks = list.ChunkIndices; Length = list.Length; _EntityComponentStore = list.EntityComponentStore; _MatchingArchetypes = matchingArchetypes; @@ -82,8 +82,10 @@ internal UnsafeChunkCacheIterator(in EntityQueryFilter filter, bool hasEnableabl public bool MoveNextChunk(ref int chunkIndexInCache, out ArchetypeChunk outputChunk, out int outputChunkEntityCount, out byte outputUseEnableBits, ref v128 enableBits) { #if UNITY_BURST_EXPERIMENTAL_PREFETCH_INTRINSIC - if (Burst.CompilerServices.Hint.Likely(chunkIndexInCache + 1 < Length)) - Common.Prefetch(_Chunks[chunkIndexInCache + 1], Common.ReadWrite.Read); + if (Hint.Likely(chunkIndexInCache + 1 < Length)) + { + Common.Prefetch(&EntityComponentStore.PerChunkArray.ChunkData[_Chunks[chunkIndexInCache + 1]], Common.ReadWrite.Read); + } #endif chunkIndexInCache++; diff --git a/Unity.Entities/Journaling/EntitiesJournaling+Preferences.cs b/Unity.Entities/Journaling/EntitiesJournaling+Preferences.cs index 5484597..cbf461b 100644 --- a/Unity.Entities/Journaling/EntitiesJournaling+Preferences.cs +++ b/Unity.Entities/Journaling/EntitiesJournaling+Preferences.cs @@ -1,9 +1,7 @@ #if (UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING -#if !UNITY_DOTSRUNTIME using Unity.Mathematics; using UnityEngine; -#endif namespace Unity.Entities { @@ -20,11 +18,9 @@ public static class Preferences internal const int k_TotalMemoryMBMax = 1024; internal const bool k_PostProcessDefault = true; -#if !UNITY_DOTSRUNTIME const string k_EnabledKey = nameof(EntitiesJournaling) + "." + nameof(Enabled); const string k_TotalMemoryMBKey = nameof(EntitiesJournaling) + nameof(TotalMemoryMB); const string k_PostProcessKey = nameof(EntitiesJournaling) + "." + nameof(PostProcess); -#endif /// /// Whether or not entities journaling events are recorded. @@ -33,15 +29,11 @@ public static class Preferences /// This value is only read during journaling initialization. /// The new value will take effect when journaling initializes again. /// -#if !UNITY_DOTSRUNTIME public static bool Enabled { get => PlayerPrefs.GetInt(k_EnabledKey, k_EnabledDefault ? 1 : 0) == 1; set => PlayerPrefs.SetInt(k_EnabledKey, value ? 1 : 0); } -#else - public static bool Enabled { get; set; } = k_EnabledDefault; -#endif /// /// Total amount of memory in megabytes allocated for journaling. @@ -50,29 +42,21 @@ public static bool Enabled /// This value is only read during journaling initialization. /// The new value will take effect when journaling initializes again. /// -#if !UNITY_DOTSRUNTIME public static int TotalMemoryMB { get => math.clamp(PlayerPrefs.GetInt(k_TotalMemoryMBKey, k_TotalMemoryMBDefault), k_TotalMemoryMBMin, k_TotalMemoryMBMax); set => PlayerPrefs.SetInt(k_TotalMemoryMBKey, math.clamp(value, k_TotalMemoryMBMin, k_TotalMemoryMBMax)); } -#else - public static int TotalMemoryMB { get; set; } = k_TotalMemoryMBDefault; -#endif /// /// Apply post-processing to journaling records. /// Converts records into whenever possible. /// -#if !UNITY_DOTSRUNTIME public static bool PostProcess { get => PlayerPrefs.GetInt(k_PostProcessKey, k_PostProcessDefault ? 1 : 0) == 1; set => PlayerPrefs.SetInt(k_PostProcessKey, value ? 1 : 0); } -#else - public static bool PostProcess { get; set; } = k_PostProcessDefault; -#endif } } } diff --git a/Unity.Entities/Journaling/EntitiesJournaling+State.cs b/Unity.Entities/Journaling/EntitiesJournaling+State.cs index fd80d00..c472ced 100644 --- a/Unity.Entities/Journaling/EntitiesJournaling+State.cs +++ b/Unity.Entities/Journaling/EntitiesJournaling+State.cs @@ -37,7 +37,6 @@ struct SystemVersionNode SystemVersionNode* m_LastSystemVersionNode; SpinLock m_Lock; ulong m_RecordIndex; - int m_ErrorFailedResolveCount; internal JournalingState(int capacityInBytes) { @@ -50,7 +49,6 @@ internal JournalingState(int capacityInBytes) m_LastSystemVersionNode = null; m_Lock = new SpinLock(); m_RecordIndex = 0; - m_ErrorFailedResolveCount = 10; } internal int RecordCount => m_Records.Count; @@ -183,9 +181,9 @@ internal void PushBack(RecordType recordType, ulong worldSequenceNumber, in Syst } } - internal void PushBack(RecordType recordType, ulong worldSequenceNumber, in SystemHandle executingSystem, in SystemHandle originSystem, Chunk* chunks, int chunkCount, TypeIndex* types, int typeCount, void* data, int dataLength) + internal void PushBack(RecordType recordType, ulong worldSequenceNumber, in SystemHandle executingSystem, in SystemHandle originSystem, Archetype* archetype, ChunkIndex chunk, TypeIndex* types, int typeCount, void* data, int dataLength) { - var entityCount = GetEntityCount(chunks, chunkCount); + var entityCount = chunk.Count; m_Lock.Acquire(); try @@ -194,7 +192,7 @@ internal void PushBack(RecordType recordType, ulong worldSequenceNumber, in Syst return; PushBackHeader(recordType, worldSequenceNumber, in executingSystem, in originSystem, entityCount, typeCount, dataLength); - PushBackEntities(chunks, chunkCount); + PushBackEntities(archetype, chunk); PushBackTypes(types, typeCount); PushBackData(data, dataLength); } @@ -246,9 +244,9 @@ internal void PushBack(RecordType recordType, EntityComponentStore* store, uint } } - internal void PushBack(RecordType recordType, EntityComponentStore* store, uint version, in SystemHandle originSystem, Chunk* chunks, int chunkCount, TypeIndex* types, int typeCount, void* data, int dataLength) + internal void PushBack(RecordType recordType, EntityComponentStore* store, uint version, in SystemHandle originSystem, Archetype* archetype, ChunkIndex chunk, TypeIndex* types, int typeCount, void* data, int dataLength) { - var entityCount = GetEntityCount(chunks, chunkCount); + var entityCount = chunk.Count; m_Lock.Acquire(); try @@ -258,7 +256,7 @@ internal void PushBack(RecordType recordType, EntityComponentStore* store, uint var executingSystem = GetSystemHandle(store, version); PushBackHeader(recordType, store->WorldSequenceNumber, in executingSystem, in originSystem, entityCount, typeCount, dataLength); - PushBackEntities(chunks, chunkCount); + PushBackEntities(archetype, chunk); PushBackTypes(types, typeCount); PushBackData(data, dataLength); } @@ -382,8 +380,8 @@ void PushBackEntities(ArchetypeChunk* chunks, int chunkCount) { var archetypeChunk = chunks[i]; var chunk = archetypeChunk.m_Chunk; - var archetype = chunk->Archetype; - var buffer = chunk->Buffer; + var archetype = archetypeChunk.Archetype.Archetype; + var buffer = chunk.Buffer; var length = archetypeChunk.Count; var startOffset = archetype->Offsets[0]; if (!m_Buffer.PushBack(buffer + startOffset, sizeof(Entity) * length)) @@ -391,21 +389,16 @@ void PushBackEntities(ArchetypeChunk* chunks, int chunkCount) } } - void PushBackEntities(Chunk* chunks, int chunkCount) + void PushBackEntities(Archetype* archetype, ChunkIndex chunk) { - if (chunks == null || chunkCount <= 0) + if (chunk == ChunkIndex.Null) return; - for (var i = 0; i < chunkCount; ++i) - { - var chunk = chunks[i]; - var archetype = chunk.Archetype; - var buffer = chunk.Buffer; - var length = chunk.Count; - var startOffset = archetype->Offsets[0]; - if (!m_Buffer.PushBack(buffer + startOffset, sizeof(Entity) * length)) - UnityEngine.Debug.LogError($"EntitiesJournaling: Failed to push back chunk entities in buffer."); - } + var buffer = chunk.Buffer; + var length = chunk.Count; + var startOffset = archetype->Offsets[0]; + if (!m_Buffer.PushBack(buffer + startOffset, sizeof(Entity) * length)) + UnityEngine.Debug.LogError($"EntitiesJournaling: Failed to push back chunk entities in buffer."); } void PushBackTypes(TypeIndex* types, int typeCount) @@ -463,9 +456,6 @@ void PushBackData(void* data, int dataLength) m_SystemVersionNodes = node; m_LastSystemVersionNode = node; - // Initialize buffer with initial global system version with default system handle - node->Buffer->PushBack(new SystemVersionHandle { Version = ChangeVersionUtility.InitialGlobalSystemVersion, Handle = default }); - return node->Buffer; } @@ -496,11 +486,10 @@ SystemHandle GetSystemHandle(EntityComponentStore* entityComponentStore, uint gl return element.Handle; } - if (m_ErrorFailedResolveCount > 0) - { - UnityEngine.Debug.LogError($"EntitiesJournaling: Failed to resolve system handle for global system version {globalSystemVersion}."); - m_ErrorFailedResolveCount--; - } + // System handle not found for this version. + // Here it could mean an event recorded outside a system (legitimate) + // or a system handle was not recorded (bug), but we cannot know for sure. + // Because of this, we cannot throw an error here. return default; } @@ -515,18 +504,6 @@ static int GetEntityCount(ArchetypeChunk* chunks, int chunkCount) return entityCount; } - - static int GetEntityCount(Chunk* chunks, int chunkCount) - { - if (chunks == null || chunkCount <= 0) - return 0; - - var entityCount = 0; - for (var i = 0; i < chunkCount; ++i) - entityCount += chunks[i].Count; - - return entityCount; - } } } } diff --git a/Unity.Entities/Journaling/EntitiesJournaling.cs b/Unity.Entities/Journaling/EntitiesJournaling.cs index 041e736..288812a 100644 --- a/Unity.Entities/Journaling/EntitiesJournaling.cs +++ b/Unity.Entities/Journaling/EntitiesJournaling.cs @@ -202,13 +202,11 @@ internal static void AddRecord(RecordType recordType, ulong worldSequenceNumber, [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "(UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING")] [MethodImpl(MethodImplOptions.NoInlining)] - internal static void AddRecord(RecordType recordType, ulong worldSequenceNumber, in SystemHandle executingSystem, Chunk* chunks, int chunkCount, in SystemHandle originSystem = default, TypeIndex* types = null, int typeCount = 0, void* data = null, int dataLength = 0) + internal static void AddRecord(RecordType recordType, ulong worldSequenceNumber, in SystemHandle executingSystem, Archetype* archetype, ChunkIndex chunk, in SystemHandle originSystem = default, TypeIndex* types = null, int typeCount = 0, void* data = null, int dataLength = 0) { if (!s_Initialized) return; - if (chunks == null && chunkCount > 0) - chunkCount = 0; if (types == null && typeCount > 0) typeCount = 0; if (data == null && dataLength > 0) @@ -221,7 +219,7 @@ internal static void AddRecord(RecordType recordType, ulong worldSequenceNumber, typeCount--; } - s_State.PushBack(recordType, worldSequenceNumber, in executingSystem, in originSystem, chunks, chunkCount, types, typeCount, data, dataLength); + s_State.PushBack(recordType, worldSequenceNumber, in executingSystem, in originSystem, archetype, chunk, types, typeCount, data, dataLength); } [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "(UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING")] @@ -274,13 +272,11 @@ internal static void AddRecord(RecordType recordType, EntityComponentStore* enti [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "(UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING")] [MethodImpl(MethodImplOptions.NoInlining)] - internal static void AddRecord(RecordType recordType, EntityComponentStore* entityComponentStore, uint globalSystemVersion, Chunk* chunks, int chunkCount, in SystemHandle originSystem = default, TypeIndex* types = null, int typeCount = 0, void* data = null, int dataLength = 0) + internal static void AddRecord(RecordType recordType, EntityComponentStore* entityComponentStore, uint globalSystemVersion, Archetype* archetype, ChunkIndex chunk, in SystemHandle originSystem = default, TypeIndex* types = null, int typeCount = 0, void* data = null, int dataLength = 0) { if (!s_Initialized) return; - if (chunks == null && chunkCount > 0) - chunkCount = 0; if (types == null && typeCount > 0) typeCount = 0; if (data == null && dataLength > 0) @@ -293,7 +289,7 @@ internal static void AddRecord(RecordType recordType, EntityComponentStore* enti typeCount--; } - s_State.PushBack(recordType, entityComponentStore, globalSystemVersion, in originSystem, chunks, chunkCount, types, typeCount, data, dataLength); + s_State.PushBack(recordType, entityComponentStore, globalSystemVersion, in originSystem, archetype, chunk, types, typeCount, data, dataLength); } [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "(UNITY_EDITOR || DEVELOPMENT_BUILD) && !DISABLE_ENTITIES_JOURNALING")] @@ -360,7 +356,7 @@ static void UpdateState() } // Sync enabled state - var access = world.EntityManager.GetCheckedEntityDataAccess(); + var access = world.EntityManager.GetCheckedEntityDataAccessExclusive(); if (access != null) { var store = access->EntityComponentStore; @@ -380,7 +376,6 @@ static string GetWorldName(ulong worldSeqNumber) return s_WorldNameMap.TryGetValue(worldSeqNumber, out var name) ? name : string.Empty; } - [ExcludeFromBurstCompatTesting("uses managed Dictionary")] static SystemTypeIndex GetSystemType(SystemHandle handle) { diff --git a/Unity.Entities/Journaling/SpinLock.cs b/Unity.Entities/Journaling/SpinLock.cs index 9efb995..8b6fcac 100644 --- a/Unity.Entities/Journaling/SpinLock.cs +++ b/Unity.Entities/Journaling/SpinLock.cs @@ -7,9 +7,7 @@ namespace Unity.Entities.LowLevel [GenerateTestsForBurstCompatibility] internal struct SpinLock { -#if !NET_DOTS int m_Lock; -#endif /// /// Continually spin until the lock can be acquired. @@ -17,7 +15,6 @@ internal struct SpinLock [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Acquire() { -#if !NET_DOTS for (;;) { // Optimistically assume the lock is free on the first try. @@ -34,7 +31,6 @@ public void Acquire() // and you don't care about power efficiency, using the 'pause' instruction will slow down lock // acquisition in the contended scenario. } -#endif } /// @@ -44,13 +40,9 @@ public void Acquire() [MethodImpl(MethodImplOptions.AggressiveInlining)] public bool TryAcquire() { -#if !NET_DOTS // First do a memory load (read) to check if lock is free in order to prevent unnecessary cache misses. return Volatile.Read(ref m_Lock) == 0 && Interlocked.CompareExchange(ref m_Lock, 1, 0) == 0; -#else - return false; -#endif } /// @@ -75,9 +67,7 @@ public bool TryAcquire(bool spin) [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Release() { -#if !NET_DOTS Volatile.Write(ref m_Lock, 0); -#endif } } } diff --git a/Unity.Entities/LambdaJobConstruction/LambdaJobQueryConstructionMethods.cs b/Unity.Entities/LambdaJobConstruction/LambdaJobQueryConstructionMethods.cs index d58c90c..9628af2 100644 --- a/Unity.Entities/LambdaJobConstruction/LambdaJobQueryConstructionMethods.cs +++ b/Unity.Entities/LambdaJobConstruction/LambdaJobQueryConstructionMethods.cs @@ -48,6 +48,39 @@ public static class LambdaJobQueryConstructionMethods [AllowMultipleInvocations] public static ForEachLambdaJobDescription WithDisabled(this ForEachLambdaJobDescription description) => description; + /// + /// Add qualification to the generated query that it should only return entities that have all of the specified component type, + /// whether or not it is enabled on each entity. + /// + /// First type of component + /// The target object + /// The target object, suitable for chaining multiple methods + [AllowMultipleInvocations] + public static ForEachLambdaJobDescription WithPresent(this ForEachLambdaJobDescription description) => description; + + /// + /// Add qualification to the generated query that it should only return entities that have all of the specified component type, + /// whether or not it is enabled on each entity. + /// + /// First type of component + /// Second type of component + /// The target object + /// The target object, suitable for chaining multiple methods + [AllowMultipleInvocations] + public static ForEachLambdaJobDescription WithPresent(this ForEachLambdaJobDescription description) => description; + + /// + /// Add qualification to the generated query that it should only return entities that have all of the specified component type, + /// whether or not it is enabled on each entity. + /// + /// First type of component + /// Second type of component + /// Third type of component + /// The target object + /// The target object, suitable for chaining multiple methods + [AllowMultipleInvocations] + public static ForEachLambdaJobDescription WithPresent(this ForEachLambdaJobDescription description) => description; + /// /// Add qualification to the generated query that it should only return entities that either 1) do not have the specified component type OR 2) have the specified DISABLED component type. /// @@ -109,7 +142,8 @@ public static class LambdaJobQueryConstructionMethods public static ForEachLambdaJobDescription WithAny(this ForEachLambdaJobDescription description) => description; /// - /// Add qualification to the generated query that it should only return entities that have all of the specified component type. + /// Add qualification to the generated query that it should only return entities that have all of the specified component type, + /// and only if the component is enabled on the entity. /// /// First type of component /// The target object @@ -118,7 +152,8 @@ public static class LambdaJobQueryConstructionMethods public static ForEachLambdaJobDescription WithAll(this ForEachLambdaJobDescription description) => description; /// - /// Add qualification to the generated query that it should only return entities that have all of the specified component type. + /// Add qualification to the generated query that it should only return entities that have all of the specified component type, + /// and only if the component is enabled on the entity. /// /// First type of component /// Second type of component @@ -128,7 +163,8 @@ public static class LambdaJobQueryConstructionMethods public static ForEachLambdaJobDescription WithAll(this ForEachLambdaJobDescription description) => description; /// - /// Add qualification to the generated query that it should only return entities that have all of the specified component type. + /// Add qualification to the generated query that it should only return entities that have all of the specified component type, + /// and only if the component is enabled on the entity. /// /// First type of component /// Second type of component diff --git a/Unity.Entities/ManagedComponentStore.cs b/Unity.Entities/ManagedComponentStore.cs index 58151dd..1520873 100644 --- a/Unity.Entities/ManagedComponentStore.cs +++ b/Unity.Entities/ManagedComponentStore.cs @@ -259,7 +259,7 @@ private int FindNonDefaultSharedComponentIndex(TypeIndex typeIndex, int hashCode if (!m_HashLookup.TryGetFirstValue(EntityComponentStore.GetSharedComponentHashKey(typeIndex, hashCode), out itemIndex, - out iter)) + out iter)) return -1; var infos = SharedComponentInfoPtr; @@ -384,13 +384,8 @@ public T GetSharedComponentData_Managed(int index) where T : struct public object GetSharedComponentDataBoxed(int index, TypeIndex typeIndex) { -#if !NET_DOTS if (index == 0) return Activator.CreateInstance(TypeManager.GetType(typeIndex)); -#else - if (index == 0) - throw new InvalidOperationException("Implement TypeManager.GetType(typeIndex).DefaultValue"); -#endif return m_SharedComponentData[index]; } @@ -443,7 +438,7 @@ public void RemoveSharedComponentReference_Managed(int index, int numRefs = 1) if (m_HashLookup.TryGetFirstValue( EntityComponentStore.GetSharedComponentHashKey(typeIndex, hashCode), out itemIndex, - out iter)) + out iter)) { do { @@ -480,7 +475,7 @@ public void CheckInternalConsistency() if (m_HashLookup.TryGetFirstValue( EntityComponentStore.GetSharedComponentHashKey(infos[i].ComponentType, hashCode), out itemIndex, - out iter)) + out iter)) { do { @@ -579,8 +574,8 @@ public NativeArray MoveSharedComponents_Managed( for (int i = 0; i < chunks.Length; ++i) { var chunk = chunks[i].m_Chunk; - var archetype = chunk->Archetype; - var sharedComponentValues = chunk->SharedComponentValues; + var archetype = chunks[i].Archetype.Archetype; + var sharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); for (int sharedComponentIndex = 0; sharedComponentIndex < archetype->NumSharedComponents; ++sharedComponentIndex) @@ -637,9 +632,8 @@ public void PrepareForDeserialize() ResetSharedComponentData(); } - public void PatchEntities(Archetype* archetype, Chunk* chunk, int entityCount, EntityRemapUtility.EntityRemapInfo* remapping) + public void PatchEntities(Archetype* archetype, ChunkIndex chunk, int entityCount, EntityRemapUtility.EntityRemapInfo* remapping) { -#if !UNITY_DOTSRUNTIME var firstManagedComponent = archetype->FirstManagedComponent; var numManagedComponents = archetype->NumManagedComponents; @@ -649,7 +643,7 @@ public void PatchEntities(Archetype* archetype, Chunk* chunk, int entityCount, E if (!archetype->Types[indexInArchetype].HasEntityReferences) continue; - var a = (int*)ChunkDataUtility.GetComponentDataRO(chunk, 0, indexInArchetype); + var a = (int*)ChunkDataUtility.GetComponentDataRO(chunk, archetype, 0, indexInArchetype); for (int ei = 0; ei < entityCount; ++ei) { if (a[ei] != 0) @@ -661,12 +655,10 @@ public void PatchEntities(Archetype* archetype, Chunk* chunk, int entityCount, E } m_ManagedObjectRemap.ClearGCRefs(); -#endif } void PatchEntitiesForPrefab(int* managedComponents, int numManagedComponents, int allocatedCount, int remappingCount, Entity* remapSrc, Entity* remapDst) { -#if !UNITY_DOTSRUNTIME for (int i = 0; i < allocatedCount; ++i) { for (int c = 0; c < numManagedComponents; c++) @@ -682,7 +674,6 @@ void PatchEntitiesForPrefab(int* managedComponents, int numManagedComponents, in remapDst += remappingCount; } m_ManagedObjectRemap.ClearGCRefs(); -#endif } public void Playback(ref ManagedDeferredCommands managedDeferredCommands) @@ -719,7 +710,7 @@ public void Playback(ref ManagedDeferredCommands managedDeferredCommands) case (ManagedDeferredCommands.Command.PatchManagedEntities): { var archetype = (Archetype*)reader.ReadNext(); - var chunk = (Chunk*)reader.ReadNext(); + var chunk = reader.ReadNext(); var entityCount = reader.ReadNext(); var remapping = (EntityRemapUtility.EntityRemapInfo*)reader.ReadNext(); @@ -813,9 +804,7 @@ private void CloneManagedComponents(int* srcArray, int componentCount, int* dstA dstArray += instanceCount; } -#if !UNITY_DOTSRUNTIME m_ManagedObjectClone.ClearGCRefs(); -#endif } internal void SetManagedComponentValue(int index, object componentObject) @@ -877,9 +866,7 @@ public void CloneManagedComponentsFromDifferentWorld(int* indices, int count, Ma m_ManagedComponentData[dstIndex] = clone; } -#if !UNITY_DOTSRUNTIME m_ManagedObjectClone.ClearGCRefs(); -#endif } public void ResetManagedComponentStoreForDeserialization(int managedComponentCount, ref EntityComponentStore entityComponentStore) diff --git a/Unity.Entities/ProfilerModules/MemoryProfiler.cs b/Unity.Entities/ProfilerModules/MemoryProfiler.cs index 4061cdf..42b4d56 100644 --- a/Unity.Entities/ProfilerModules/MemoryProfiler.cs +++ b/Unity.Entities/ProfilerModules/MemoryProfiler.cs @@ -49,11 +49,7 @@ public void Execute() internal const string k_AllocatedMemoryCounterName = "Allocated Memory"; internal const string k_UnusedMemoryCounterName = "Unused Memory"; -#if UNITY_DOTSRUNTIME - public static bool Enabled => Profiler.enabled; -#else public static bool Enabled => Profiler.enabled && Profiler.IsCategoryEnabled(Category); -#endif public static Guid Guid { get; private set; } public static ProfilerCategory Category { get; private set; } public static BytesCounter AllocatedBytesCounter { get; private set; } diff --git a/Unity.Entities/ProfilerModules/StructuralChangesProfiler.cs b/Unity.Entities/ProfilerModules/StructuralChangesProfiler.cs index 41296f2..6bb2c1d 100644 --- a/Unity.Entities/ProfilerModules/StructuralChangesProfiler.cs +++ b/Unity.Entities/ProfilerModules/StructuralChangesProfiler.cs @@ -18,11 +18,7 @@ static partial class StructuralChangesProfiler sealed class SharedInitialized { internal static readonly SharedStatic Ref = SharedStatic.GetOrCreate(); } sealed class SharedStaticData { internal static readonly SharedStatic Ref = SharedStatic.GetOrCreate(); } -#if UNITY_DOTSRUNTIME - public static bool Enabled => Profiler.enabled; -#else public static bool Enabled => Profiler.enabled && Profiler.IsCategoryEnabled(s_Data.Category); -#endif public static Guid Guid => s_Data.Guid; public static ProfilerCategory Category => s_Data.Category; diff --git a/Unity.Entities/Properties/ManagedObjectBlobs.cs b/Unity.Entities/Properties/ManagedObjectBlobs.cs index b1b2378..7113626 100644 --- a/Unity.Entities/Properties/ManagedObjectBlobs.cs +++ b/Unity.Entities/Properties/ManagedObjectBlobs.cs @@ -2,9 +2,7 @@ using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; -#if !NET_DOTS using Unity.Properties; -#endif namespace Unity.Entities { @@ -193,10 +191,8 @@ void VisitValue(TValue value) if (null == value) return; -#if !UNITY_DOTSRUNTIME if (value is UnityEngine.Object) return; -#endif } if (!TypeTraits.IsValueType && typeof(string) != typeof(TValue)) diff --git a/Unity.Entities/Properties/ManagedObjectClone.cs b/Unity.Entities/Properties/ManagedObjectClone.cs index 4cfdc7e..79c1393 100644 --- a/Unity.Entities/Properties/ManagedObjectClone.cs +++ b/Unity.Entities/Properties/ManagedObjectClone.cs @@ -2,13 +2,10 @@ using System.Collections; using System.Collections.Generic; using System.Runtime.CompilerServices; -#if !NET_DOTS using Unity.Properties; -#endif namespace Unity.Entities { -#if !NET_DOTS /// /// Unity.Properties visitor used to deep clone object instances. This is an internal class. /// @@ -62,11 +59,9 @@ public object Clone(object obj) var type = obj.GetType(); -#if !UNITY_DOTSRUNTIME // UnityEngine references are always by reference. if (typeof(UnityEngine.Object).IsAssignableFrom(type)) return obj; -#endif var instance = type.IsArray ? Array.CreateInstance(type.GetElementType(), obj is IList srcList ? srcList.Count : 0) : Activator.CreateInstance(type); @@ -256,14 +251,13 @@ void CloneValue(ref TValue dstValue, TValue srcValue) { var type = srcValue.GetType(); -#if !UNITY_DOTSRUNTIME // UnityEngine references can be copied as-is. if (typeof(UnityEngine.Object).IsAssignableFrom(type)) { dstValue = srcValue; return; } -#endif + // Boxed value types can be copied as-is. if (!TypeTraits.IsContainer(type)) { @@ -340,19 +334,4 @@ public void ClearGCRefs() m_RootDestination = null; } } -#else - class ManagedObjectClone - { - public object Clone(object obj) - { - if (obj is ICloneable cloneable) return cloneable.Clone(); - return obj; - } - - public void ClearGCRefs() - { - //no-op, no references to clear - } - } -#endif } diff --git a/Unity.Entities/Properties/ManagedObjectEquals.cs b/Unity.Entities/Properties/ManagedObjectEquals.cs index 9160904..f688891 100644 --- a/Unity.Entities/Properties/ManagedObjectEquals.cs +++ b/Unity.Entities/Properties/ManagedObjectEquals.cs @@ -1,12 +1,9 @@ using System.Collections.Generic; using System.Runtime.CompilerServices; -#if !NET_DOTS using Unity.Properties; -#endif namespace Unity.Entities { -#if !UNITY_DOTSRUNTIME /// /// Unity.Properties visitor used to deep compare two object instances. This is an internal class. /// @@ -61,10 +58,9 @@ public bool CompareEqual(object lhs, object rhs) var type = lhs.GetType(); -#if !UNITY_DOTSRUNTIME if (typeof(UnityEngine.Object).IsAssignableFrom(type)) return lhs.Equals(rhs); -#endif + var properties = PropertyBag.GetPropertyBag(type); if (null == properties) @@ -255,13 +251,12 @@ bool CompareEquality(TValue lhs, TValue rhs) var type = lhs.GetType(); -#if !UNITY_DOTSRUNTIME // UnityEngine references can be copied as-is. if (typeof(UnityEngine.Object).IsAssignableFrom(type)) { return EqualityComparer.Default.Equals(lhs, rhs); } -#endif + // Boxed value types can be compared as-is using the default comparer (with boxing). if (!TypeTraits.IsContainer(type)) { @@ -299,15 +294,4 @@ bool CompareEquality(TValue lhs, TValue rhs) return m_Equals; } } -#else - class ManagedObjectEqual - { - public bool CompareEqual(object lhs, object rhs) - { - if (lhs == null) return rhs == null; - if (rhs == null) return false; - return lhs.Equals(rhs); - } - } -#endif } diff --git a/Unity.Entities/Properties/ManagedObjectRemap.cs b/Unity.Entities/Properties/ManagedObjectRemap.cs index 46f4b73..600286b 100644 --- a/Unity.Entities/Properties/ManagedObjectRemap.cs +++ b/Unity.Entities/Properties/ManagedObjectRemap.cs @@ -1,12 +1,9 @@ using System; -#if !NET_DOTS using System.Collections.Generic; using Unity.Properties; -#endif namespace Unity.Entities { -#if !UNITY_DOTSRUNTIME interface ITypedVisit { void Visit(Property property, ref TContainer container, ref TValue value); @@ -131,10 +128,8 @@ void IPropertyVisitor.Visit(Property pro return; } -#if !UNITY_DOTSRUNTIME if (value is UnityEngine.Object) return; -#endif if (this is ITypedVisit typed) { @@ -169,23 +164,4 @@ public void ClearGCRefs() m_References?.Clear(); } } -#else - unsafe class ManagedObjectRemap - { - public void RemapEntityReferences(object obj, EntityRemapUtility.EntityRemapInfo* entityRemapInfo) - { - // Not supported in DOTS Runtime. - } - - public void RemapEntityReferencesForPrefab(object obj, Entity* remapSrc, Entity* remapDst, int remapInfoCount) - { - // Not supported in DOTS Runtime. - } - - public void ClearGCRefs() - { - //no-op, no references to clear - } - } -#endif } diff --git a/Unity.Entities/QueryEnumerable/QueryEnumerable.gen.cs b/Unity.Entities/QueryEnumerable/QueryEnumerable.gen.cs index 78a5f21..18b8552 100644 --- a/Unity.Entities/QueryEnumerable/QueryEnumerable.gen.cs +++ b/Unity.Entities/QueryEnumerable/QueryEnumerable.gen.cs @@ -17,7 +17,9 @@ public struct QueryEnumerable : IEnumerable /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -26,7 +28,10 @@ public struct QueryEnumerable : IEnumerable /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -36,7 +41,11 @@ public struct QueryEnumerable : IEnumerable /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + where TComponent3 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must NOT be present. @@ -66,7 +75,7 @@ public struct QueryEnumerable : IEnumerable public QueryEnumerable WithAbsent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -74,7 +83,7 @@ public struct QueryEnumerable : IEnumerable public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -83,7 +92,7 @@ public struct QueryEnumerable : IEnumerable public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -93,7 +102,7 @@ public struct QueryEnumerable : IEnumerable public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -101,7 +110,7 @@ public struct QueryEnumerable : IEnumerable public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -110,7 +119,7 @@ public struct QueryEnumerable : IEnumerable public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -120,7 +129,7 @@ public struct QueryEnumerable : IEnumerable public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -128,7 +137,7 @@ public struct QueryEnumerable : IEnumerable public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -137,7 +146,7 @@ public struct QueryEnumerable : IEnumerable public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -146,6 +155,33 @@ public struct QueryEnumerable : IEnumerable /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select components in chunks in which the specified component might have changed since the last time the system updated. /// @@ -175,6 +211,31 @@ public struct QueryEnumerable : IEnumerable /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithOptions(EntityQueryOptions options) => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Only select chunks that have a specified value for a managed shared component. + /// + /// The managed shared component type + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent) + where TSharedComponent1 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Only select chunks that have the specified values for two managed shared components. + /// + /// The first managed shared component type + /// The second managed shared component type + /// The value of which an entity must have in order to match this query + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) + where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent2 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select chunks that have a specified value for a shared component. /// @@ -183,7 +244,7 @@ public struct QueryEnumerable : IEnumerable /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent) - where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -196,8 +257,8 @@ public QueryEnumerable WithSharedComponentFilter(TSharedC /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) - where TSharedComponent1 : struct, ISharedComponentData - where TSharedComponent2 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData + where TSharedComponent2 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -416,7 +477,9 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -425,7 +488,10 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -435,7 +501,11 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + where TComponent3 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must NOT be present. @@ -465,7 +535,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> public QueryEnumerable WithAbsent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -473,7 +543,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -482,7 +552,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -492,7 +562,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -500,7 +570,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -509,7 +579,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -519,7 +589,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -527,7 +597,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -536,7 +606,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -545,6 +615,33 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select components in chunks in which the specified component might have changed since the last time the system updated. /// @@ -574,6 +671,31 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithOptions(EntityQueryOptions options) => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Only select chunks that have a specified value for a managed shared component. + /// + /// The managed shared component type + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent) + where TSharedComponent1 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Only select chunks that have the specified values for two managed shared components. + /// + /// The first managed shared component type + /// The second managed shared component type + /// The value of which an entity must have in order to match this query + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) + where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent2 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select chunks that have a specified value for a shared component. /// @@ -582,7 +704,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2)> /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent) - where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -595,8 +717,8 @@ public QueryEnumerable WithSharedComponentFilter(TSha /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) - where TSharedComponent1 : struct, ISharedComponentData - where TSharedComponent2 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData + where TSharedComponent2 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -823,7 +945,9 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -832,7 +956,10 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -842,7 +969,11 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + where TComponent3 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must NOT be present. @@ -872,7 +1003,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> public QueryEnumerable WithAbsent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -880,7 +1011,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -889,7 +1020,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -899,7 +1030,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -907,7 +1038,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -916,7 +1047,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -926,7 +1057,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -934,7 +1065,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -943,7 +1074,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -952,6 +1083,33 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select components in chunks in which the specified component might have changed since the last time the system updated. /// @@ -981,6 +1139,31 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithOptions(EntityQueryOptions options) => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Only select chunks that have a specified value for a managed shared component. + /// + /// The managed shared component type + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent) + where TSharedComponent1 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Only select chunks that have the specified values for two managed shared components. + /// + /// The first managed shared component type + /// The second managed shared component type + /// The value of which an entity must have in order to match this query + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) + where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent2 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select chunks that have a specified value for a shared component. /// @@ -989,7 +1172,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3)> /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent) - where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -1002,8 +1185,8 @@ public QueryEnumerable WithSharedComponentFilter( /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) - where TSharedComponent1 : struct, ISharedComponentData - where TSharedComponent2 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData + where TSharedComponent2 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -1238,7 +1421,9 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -1247,7 +1432,10 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -1257,7 +1445,11 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + where TComponent3 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must NOT be present. @@ -1287,7 +1479,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> public QueryEnumerable WithAbsent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -1295,7 +1487,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -1304,7 +1496,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -1314,7 +1506,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -1322,7 +1514,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -1331,7 +1523,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -1341,7 +1533,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -1349,7 +1541,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -1358,7 +1550,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -1367,6 +1559,33 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select components in chunks in which the specified component might have changed since the last time the system updated. /// @@ -1396,6 +1615,31 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithOptions(EntityQueryOptions options) => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Only select chunks that have a specified value for a managed shared component. + /// + /// The managed shared component type + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent) + where TSharedComponent1 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Only select chunks that have the specified values for two managed shared components. + /// + /// The first managed shared component type + /// The second managed shared component type + /// The value of which an entity must have in order to match this query + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) + where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent2 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select chunks that have a specified value for a shared component. /// @@ -1404,7 +1648,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4)> /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent) - where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -1417,8 +1661,8 @@ public QueryEnumerable WithSharedComponentFilterQueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) - where TSharedComponent1 : struct, ISharedComponentData - where TSharedComponent2 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData + where TSharedComponent2 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -1661,7 +1905,9 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -1670,7 +1916,10 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -1680,7 +1929,11 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + where TComponent3 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must NOT be present. @@ -1710,7 +1963,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, public QueryEnumerable WithAbsent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -1718,7 +1971,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -1727,7 +1980,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -1737,7 +1990,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -1745,7 +1998,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -1754,7 +2007,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -1764,7 +2017,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -1772,7 +2025,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -1781,7 +2034,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -1790,6 +2043,33 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select components in chunks in which the specified component might have changed since the last time the system updated. /// @@ -1819,6 +2099,31 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithOptions(EntityQueryOptions options) => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Only select chunks that have a specified value for a managed shared component. + /// + /// The managed shared component type + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent) + where TSharedComponent1 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Only select chunks that have the specified values for two managed shared components. + /// + /// The first managed shared component type + /// The second managed shared component type + /// The value of which an entity must have in order to match this query + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) + where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent2 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select chunks that have a specified value for a shared component. /// @@ -1827,7 +2132,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, T4, /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent) - where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -1840,8 +2145,8 @@ public QueryEnumerable WithSharedComponentFilterQueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) - where TSharedComponent1 : struct, ISharedComponentData - where TSharedComponent2 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData + where TSharedComponent2 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -2092,7 +2397,9 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -2101,7 +2408,10 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -2111,7 +2421,11 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + where TComponent3 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must NOT be present. @@ -2141,7 +2455,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, public QueryEnumerable WithAbsent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -2149,7 +2463,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -2158,7 +2472,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -2168,7 +2482,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -2176,7 +2490,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -2185,7 +2499,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -2195,7 +2509,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -2203,7 +2517,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -2212,7 +2526,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -2221,6 +2535,33 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select components in chunks in which the specified component might have changed since the last time the system updated. /// @@ -2250,6 +2591,31 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithOptions(EntityQueryOptions options) => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Only select chunks that have a specified value for a managed shared component. + /// + /// The managed shared component type + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent) + where TSharedComponent1 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Only select chunks that have the specified values for two managed shared components. + /// + /// The first managed shared component type + /// The second managed shared component type + /// The value of which an entity must have in order to match this query + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) + where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent2 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select chunks that have a specified value for a shared component. /// @@ -2258,7 +2624,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, T3, /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent) - where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -2271,8 +2637,8 @@ public QueryEnumerable WithSharedComponentFilterQueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) - where TSharedComponent1 : struct, ISharedComponentData - where TSharedComponent2 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData + where TSharedComponent2 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -2531,7 +2897,9 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -2540,7 +2908,10 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -2550,7 +2921,11 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + where TComponent3 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must NOT be present. @@ -2580,7 +2955,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, public QueryEnumerable WithAbsent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -2588,7 +2963,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -2597,7 +2972,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -2607,7 +2982,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, public QueryEnumerable WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -2615,7 +2990,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -2624,7 +2999,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -2634,7 +3009,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, public QueryEnumerable WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -2642,7 +3017,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -2651,7 +3026,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -2660,6 +3035,33 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select components in chunks in which the specified component might have changed since the last time the system updated. /// @@ -2689,6 +3091,31 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithOptions(EntityQueryOptions options) => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Only select chunks that have a specified value for a managed shared component. + /// + /// The managed shared component type + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent) + where TSharedComponent1 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Only select chunks that have the specified values for two managed shared components. + /// + /// The first managed shared component type + /// The second managed shared component type + /// The value of which an entity must have in order to match this query + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) + where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent2 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select chunks that have a specified value for a shared component. /// @@ -2697,7 +3124,7 @@ public struct QueryEnumerable : IEnumerable<(T1, T2, /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent) - where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -2710,8 +3137,8 @@ public QueryEnumerable WithSharedComponentFilterQueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable WithSharedComponentFilter(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) - where TSharedComponent1 : struct, ISharedComponentData - where TSharedComponent2 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData + where TSharedComponent2 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// diff --git a/Unity.Entities/QueryEnumerable/QueryEnumerable.tt b/Unity.Entities/QueryEnumerable/QueryEnumerable.tt index b0acc85..0b847c3 100644 --- a/Unity.Entities/QueryEnumerable/QueryEnumerable.tt +++ b/Unity.Entities/QueryEnumerable/QueryEnumerable.tt @@ -71,7 +71,9 @@ namespace Unity.Entities /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable<<#=typeParamString#>> WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable<<#=typeParamString#>> WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -80,7 +82,10 @@ namespace Unity.Entities /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable<<#=typeParamString#>> WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable<<#=typeParamString#>> WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must be present AND disabled. @@ -90,7 +95,11 @@ namespace Unity.Entities /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. - public QueryEnumerable<<#=typeParamString#>> WithDisabled() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + public QueryEnumerable<<#=typeParamString#>> WithDisabled() + where TComponent1 : unmanaged, IEnableableComponent, IComponentData + where TComponent2 : unmanaged, IEnableableComponent, IComponentData + where TComponent3 : unmanaged, IEnableableComponent, IComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// /// Specify all read-only component types that must NOT be present. @@ -120,7 +129,7 @@ namespace Unity.Entities public QueryEnumerable<<#=typeParamString#>> WithAbsent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -128,7 +137,7 @@ namespace Unity.Entities public QueryEnumerable<<#=typeParamString#>> WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -137,7 +146,7 @@ namespace Unity.Entities public QueryEnumerable<<#=typeParamString#>> WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present AND enabled. /// /// A component type /// A component type @@ -147,7 +156,7 @@ namespace Unity.Entities public QueryEnumerable<<#=typeParamString#>> WithAll() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -155,7 +164,7 @@ namespace Unity.Entities public QueryEnumerable<<#=typeParamString#>> WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -164,7 +173,7 @@ namespace Unity.Entities public QueryEnumerable<<#=typeParamString#>> WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify optional read-only component types. + /// Specify optional read-only component types (which must be enabled). /// /// Optional component /// Optional component @@ -174,7 +183,7 @@ namespace Unity.Entities public QueryEnumerable<<#=typeParamString#>> WithAny() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. @@ -182,7 +191,7 @@ namespace Unity.Entities public QueryEnumerable<<#=typeParamString#>> WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -191,7 +200,7 @@ namespace Unity.Entities public QueryEnumerable<<#=typeParamString#>> WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// - /// Specify component types that must be absent. + /// Specify component types that must be absent, or present and disabled. /// /// Absent component /// Absent component @@ -200,6 +209,33 @@ namespace Unity.Entities /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable<<#=typeParamString#>> WithNone() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable<<#=typeParamString#>> WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable<<#=typeParamString#>> WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Specify all read-only component types that must be present, whether enabled or disabled. + /// + /// A component type + /// A component type + /// A component type + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable<<#=typeParamString#>> WithPresent() => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select components in chunks in which the specified component might have changed since the last time the system updated. /// @@ -229,6 +265,31 @@ namespace Unity.Entities /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable<<#=typeParamString#>> WithOptions(EntityQueryOptions options) => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// + /// Only select chunks that have a specified value for a managed shared component. + /// + /// The managed shared component type + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable<<#=typeParamString#>> WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent) + where TSharedComponent1 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + + /// + /// Only select chunks that have the specified values for two managed shared components. + /// + /// The first managed shared component type + /// The second managed shared component type + /// The value of which an entity must have in order to match this query + /// The value of which an entity must have in order to match this query + /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. + /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. + public QueryEnumerable<<#=typeParamString#>> WithSharedComponentFilterManaged(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) + where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent2 : struct, ISharedComponentData + => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); + /// /// Only select chunks that have a specified value for a shared component. /// @@ -237,7 +298,7 @@ namespace Unity.Entities /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable<<#=typeParamString#>> WithSharedComponentFilter(TSharedComponent1 sharedComponent) - where TSharedComponent1 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// @@ -250,8 +311,8 @@ namespace Unity.Entities /// QueryEnumerable, which allows enumerating over all Aspects, RefRO, RefRW, EnabledRefRO and EnabledRefRW components that match the constructed Query. /// Exception indicating that this method invocation should have been rewritten/replaced during source-generation. public QueryEnumerable<<#=typeParamString#>> WithSharedComponentFilter(TSharedComponent1 sharedComponent1, TSharedComponent2 sharedComponent2) - where TSharedComponent1 : struct, ISharedComponentData - where TSharedComponent2 : struct, ISharedComponentData + where TSharedComponent1 : unmanaged, ISharedComponentData + where TSharedComponent2 : unmanaged, ISharedComponentData => throw Internal.InternalCompilerInterface.ThrowCodeGenException(); /// diff --git a/Unity.Entities/RateUtils.cs b/Unity.Entities/RateUtils.cs index 507ad09..5535c0f 100644 --- a/Unity.Entities/RateUtils.cs +++ b/Unity.Entities/RateUtils.cs @@ -13,7 +13,10 @@ public interface IFixedRateManager { /// Obsolete. Use instead. /// The system group to check - /// True if should update its member systems, or false if the group should skip its update. + /// + /// True while the should update its member systems, and then false once the group should no longer update for this frame. + /// Note: This is called infinitely until it returns false. + /// bool ShouldGroupUpdate(ComponentSystemGroup group); /// Obsolete. Use instead. float Timestep { get; set; } @@ -26,10 +29,13 @@ public interface IFixedRateManager public interface IRateManager { /// - /// Determines whether a system group should proceed with its update. + /// Determines whether a system group should proceed with its update, and also how many times it should update per frame. /// /// The system group to check - /// True if should update its member systems, or false if the group should skip its update. + /// + /// True while the should update its member systems, and then false once the group should no longer update for this frame. + /// Note: This is called infinitely until it returns false. + /// bool ShouldGroupUpdate(ComponentSystemGroup group); /// /// The timestep since the previous group update (in seconds). diff --git a/Unity.Entities/RuntimeApplication.cs b/Unity.Entities/RuntimeApplication.cs index 3115fe3..6f070e8 100644 --- a/Unity.Entities/RuntimeApplication.cs +++ b/Unity.Entities/RuntimeApplication.cs @@ -1,9 +1,7 @@ using System; -#if !UNITY_DOTSRUNTIME using System.Linq; using UnityEngine.LowLevel; -#endif namespace Unity.Entities { @@ -22,7 +20,6 @@ internal static class RuntimeApplication internal static void InvokePreFrameUpdate() => PreFrameUpdate?.Invoke(); internal static void InvokePostFrameUpdate() => PostFrameUpdate?.Invoke(); -#if !UNITY_DOTSRUNTIME sealed class UpdatePreFrame { } sealed class UpdatePostFrame { } @@ -59,6 +56,5 @@ internal static void UnregisterFrameUpdateToCurrentPlayerLoop() playerLoop.subSystemList = playerLoopSystems.ToArray(); PlayerLoop.SetPlayerLoop(playerLoop); } -#endif } } diff --git a/Unity.Entities/ScriptBehaviourUpdateOrder.cs b/Unity.Entities/ScriptBehaviourUpdateOrder.cs index e82e424..f4a7468 100644 --- a/Unity.Entities/ScriptBehaviourUpdateOrder.cs +++ b/Unity.Entities/ScriptBehaviourUpdateOrder.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; -#if !UNITY_DOTSRUNTIME using UnityEngine.PlayerLoop; using UnityEngine.LowLevel; -#endif namespace Unity.Entities { @@ -175,7 +173,6 @@ public UpdateInGroupAttribute(Type groupType) public Type GroupType { get; } } -#if !UNITY_DOTSRUNTIME /// /// Contains helpers to add and remove systems to the UnityEngine player loop. /// @@ -528,5 +525,4 @@ public unsafe void TriggerUpdate() } } } -#endif } diff --git a/Unity.Entities/Serialization/BinarySerialization.cs b/Unity.Entities/Serialization/BinarySerialization.cs index c0aa341..c6318d4 100644 --- a/Unity.Entities/Serialization/BinarySerialization.cs +++ b/Unity.Entities/Serialization/BinarySerialization.cs @@ -1,8 +1,6 @@ using System; -#if !NET_DOTS using System.IO; using Unity.Assertions; -#endif using Unity.IO.LowLevel.Unsafe; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; @@ -205,7 +203,6 @@ public static void ReadArray(this BinaryReader reader, NativeArray element } } -#if !NET_DOTS internal unsafe class StreamBinaryReader : BinaryReader { internal string FilePath { get; } @@ -323,7 +320,6 @@ public void WriteBytes(void* data, int bytes) public long Length => stream.Length; } -#endif /// /// Provides a writer to write primitive types to a binary buffer in memory. diff --git a/Unity.Entities/Serialization/EntityPrefabReference.cs b/Unity.Entities/Serialization/EntityPrefabReference.cs index a569d39..3d6c264 100644 --- a/Unity.Entities/Serialization/EntityPrefabReference.cs +++ b/Unity.Entities/Serialization/EntityPrefabReference.cs @@ -59,7 +59,7 @@ public EntityPrefabReference(GameObject prefab) : this( public Hash128 AssetGUID => Id.GlobalId.AssetGUID; /// - /// Returns true if the reference has a valid id. In the editor, additional checks for the correct GenerationType and the existence of the referenced asset are performed. + /// Returns true if the reference has a valid id. In the editor, additional checks for the correct GenerationType and the existence of the referenced asset are performed. /// public bool IsReferenceValid { diff --git a/Unity.Entities/Serialization/ManagedObjectBinarySerialization.cs b/Unity.Entities/Serialization/ManagedObjectBinarySerialization.cs index 968cac7..5f7ae5b 100644 --- a/Unity.Entities/Serialization/ManagedObjectBinarySerialization.cs +++ b/Unity.Entities/Serialization/ManagedObjectBinarySerialization.cs @@ -12,7 +12,6 @@ namespace Unity.Entities.Serialization { -#if !UNITY_DOTSRUNTIME struct SerializedKeyFrame { public float Time; @@ -47,7 +46,6 @@ public static implicit operator SerializedKeyFrame(UnityEngine.Keyframe kf) return new SerializedKeyFrame(kf); } } -#endif // !UNITY_DOTSRUNTIME /// /// Writer to write managed objects to a stream. @@ -56,9 +54,7 @@ public static implicit operator SerializedKeyFrame(UnityEngine.Keyframe kf) /// This is used as a wrapper around with a custom layer for . /// unsafe class ManagedObjectBinaryWriter : Unity.Serialization.Binary.IContravariantBinaryAdapter -#if !UNITY_DOTSRUNTIME , IBinaryAdapter -#endif { static readonly UnityEngine.Object[] s_EmptyUnityObjectTable = new UnityEngine.Object[0]; @@ -136,7 +132,6 @@ public void WriteObject(object obj) throw new InvalidOperationException($"Deserialize should never be invoked by {nameof(ManagedObjectBinaryWriter)}"); } -#if !UNITY_DOTSRUNTIME void IBinaryAdapter.Serialize(in BinarySerializationContext context, UnityEngine.AnimationCurve value) { if (value != null) @@ -160,7 +155,6 @@ public void WriteObject(object obj) { throw new InvalidOperationException($"Deserialize should never be invoked by {nameof(ManagedObjectBinaryWriter)}"); } -#endif // !UNITY_DOTSRUNTIME } /// @@ -170,9 +164,7 @@ public void WriteObject(object obj) /// This is used as a wrapper around with a custom layer for . /// unsafe class ManagedObjectBinaryReader : Unity.Serialization.Binary.IContravariantBinaryAdapter -#if !UNITY_DOTSRUNTIME , IBinaryAdapter -#endif { readonly UnsafeAppendBuffer.Reader* m_Stream; readonly BinarySerializationParameters m_Params; @@ -236,7 +228,6 @@ public object ReadObject(Type type) return m_UnityObjects[index]; } -#if !UNITY_DOTSRUNTIME void IBinaryAdapter.Serialize(in BinarySerializationContext context, UnityEngine.AnimationCurve value) { throw new InvalidOperationException($"Serialize should never be invoked by {nameof(ManagedObjectBinaryReader)}."); @@ -262,6 +253,5 @@ public object ReadObject(Type type) return null; } -#endif // !UNITY_DOTSRUNTIME } } diff --git a/Unity.Entities/Serialization/SerializeUtility.cs b/Unity.Entities/Serialization/SerializeUtility.cs index a667a61..8bc0418 100644 --- a/Unity.Entities/Serialization/SerializeUtility.cs +++ b/Unity.Entities/Serialization/SerializeUtility.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -#if !UNITY_DOTSRUNTIME using System.Reflection; -#endif using Unity.Serialization.Binary; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; @@ -13,9 +11,6 @@ using Unity.IO.LowLevel.Unsafe; using Unity.Mathematics; -// Remove this once DOTSRuntime can use Unity.Properties -[assembly: InternalsVisibleTo("Unity.Entities.Runtime.Build")] - namespace Unity.Entities.Serialization { struct StableArchetypeCompare : IComparer @@ -50,12 +45,12 @@ internal static int AllocAndQueueReadChunkCommands(long readOffset, int totalChu } ReadCommand cmd; - cmd.Buffer = chunks; + cmd.Buffer = chunks.GetPtr(); cmd.Offset = readOffset; cmd.Size = Chunk.kChunkSize * allocatedCount; readCommands->Add(cmd); - megaChunkInfo->Add(new SerializeUtility.MegaChunkInfo((byte*)chunks, allocatedCount)); + megaChunkInfo->Add(new SerializeUtility.MegaChunkInfo(chunks, allocatedCount)); chunkCountRead += allocatedCount; readOffset += Chunk.kChunkSize * allocatedCount; } @@ -64,9 +59,9 @@ internal static int AllocAndQueueReadChunkCommands(long readOffset, int totalChu } [BurstCompile] - internal static void AddExistingChunk(Chunk* chunk, int* sharedComponentIndices, byte* enabledBitsValuesForChunk, int* perComponentDisabledBitCount) + internal static void AddExistingChunk(Archetype* archetype, ChunkIndex chunk, int* sharedComponentIndices, byte* enabledBitsValuesForChunk, int* perComponentDisabledBitCount) { - ChunkDataUtility.AddExistingChunk(chunk, sharedComponentIndices, enabledBitsValuesForChunk, perComponentDisabledBitCount); + ChunkDataUtility.AddExistingChunk(archetype, chunk, sharedComponentIndices, enabledBitsValuesForChunk, perComponentDisabledBitCount); } [BurstCompile] @@ -93,18 +88,38 @@ internal static void ImportChunks( throw new InvalidOperationException("Internal deserialization error: total chunk count doesn't match"); } + // The entity counts for each chuck are stored in the chunk metadata for serialization. + // They need to be hoisted out and stored in the global per chunk metadata array. + + var megaChunkInfoList = status->MegaChunkInfoList; + var megaChunkInfoLength = megaChunkInfoList.Length; + for (int i = 0; i < megaChunkInfoLength; i++) + { + var chunkCount = megaChunkInfoList[i].MegaChunkSize; + var megaChunkIndex = megaChunkInfoList[i].MegaChunkIndex; + var chunkPointer = megaChunkIndex.GetPtr(); + + for (int j = 0; j < chunkCount; j++) + { + var chunkIndex = new ChunkIndex(megaChunkIndex + j); + chunkIndex.Count = chunkPointer->CountForSerialization; + chunkPointer = (Chunk*)((byte*)chunkPointer + Chunk.kChunkSize); + } + } + var curMegaChunkIndex = 0; var megaChunkInfo = status->MegaChunkInfoList; var chunkLeftToLoad = megaChunkInfo[0].MegaChunkSize; - var chunk = (Chunk*) megaChunkInfo[0].MegaChunkAddress; + var chunk = megaChunkInfo[0].MegaChunkIndex; var sharedComponentArraysIndex = 0; var enabledBitsForChunk = componentEnabledBits; var enabledBitsHierarchicalDataForChunk = enabledBitsHierarchicalData; var remapedSharedComponentValues = stackalloc int[EntityComponentStore.kMaxSharedComponentCount]; for (int i = 0; i < totalChunkCount; ++i) { - var archetype = chunk->Archetype = archetypes->ElementAt((int) chunk->Archetype).Archetype; - var numSharedComponentsInArchetype = chunk->Archetype->NumSharedComponents; + var archetype = archetypes->ElementAt(chunk.GetPtr()->ArchetypeIndexForSerialization).Archetype; + ecs->SetArchetype(chunk, archetype); + var numSharedComponentsInArchetype = archetype->NumSharedComponents; int* sharedComponentValueArray = sharedComponentArray + sharedComponentArraysIndex; for (int j = 0; j < numSharedComponentsInArchetype; ++j) @@ -129,7 +144,7 @@ internal static void ImportChunks( { var chunkOffset = bufferReader.ReadInt(); var allocSizeBytes = bufferReader.ReadInt(); - var target = (BufferHeader*) OffsetFromPointer(chunk->Buffer, chunkOffset); + var target = (BufferHeader*) OffsetFromPointer(chunk.Buffer, chunkOffset); // TODO: Alignment target->Pointer = (byte*) Memory.Unmanaged.Allocate(allocSizeBytes, 8, Allocator.Persistent); @@ -141,26 +156,29 @@ internal static void ImportChunks( if (totalBlobAssetSize != 0 && archetype->HasBlobAssetRefs) { blobAssetRefChunks->Add(new ArchetypeChunk(chunk, ecs)); - PatchBlobAssetsInChunkAfterLoad(chunk, (byte*) blobAssetBuffer); + PatchBlobAssetsInChunkAfterLoad(archetype, chunk.Buffer, chunk.Count, (byte*) blobAssetBuffer); } - chunk->SequenceNumber = ecs->AllocateSequenceNumber(); + chunk.SequenceNumber = ecs->AllocateSequenceNumber(); - SerializeUtilityInterop.AddExistingChunk(chunk, remapedSharedComponentValues, enabledBitsForChunk, enabledBitsHierarchicalDataForChunk); + SerializeUtilityInterop.AddExistingChunk(archetype, chunk, remapedSharedComponentValues, enabledBitsForChunk, enabledBitsHierarchicalDataForChunk); enabledBitsForChunk += archetype->Chunks.ComponentEnabledBitsSizeTotalPerChunk; enabledBitsHierarchicalDataForChunk += archetype->TypesCount; - if (chunk->metaChunkEntity != Entity.Null) + if (chunk.MetaChunkEntity != Entity.Null) { chunksWithMetaChunkEntities->Add(new ArchetypeChunk(chunk, ecs)); } - chunk = (Chunk*) ((byte*) chunk + Chunk.kChunkSize); if (--chunkLeftToLoad == 0 && (i + 1) != totalChunkCount) { - chunk = (Chunk*) megaChunkInfo[++curMegaChunkIndex].MegaChunkAddress; + chunk = megaChunkInfo[++curMegaChunkIndex].MegaChunkIndex; chunkLeftToLoad = megaChunkInfo[curMegaChunkIndex].MegaChunkSize; } + else + { + chunk = new ChunkIndex(chunk + 1); + } } } static void RemapSharedComponentIndices(int* destValues, Archetype* archetype, int* remappedIndices, int* sourceValues) @@ -178,11 +196,9 @@ static void RemapSharedComponentIndices(int* destValues, Archetype* archetype, i return ((byte*)ptr) + offset; } - private static void PatchBlobAssetsInChunkAfterLoad(Chunk* chunk, byte* allBlobAssetData) + private static void PatchBlobAssetsInChunkAfterLoad(Archetype* archetype, byte* chunkBuffer, int entityCount, byte* allBlobAssetData) { - var archetype = chunk->Archetype; var typeCount = archetype->TypesCount; - var entityCount = chunk->Count; for (var unordered_ti = 0; unordered_ti < typeCount; ++unordered_ti) { var ti = archetype->TypeMemoryOrderIndexToIndexInArchetype[unordered_ti]; @@ -196,7 +212,6 @@ private static void PatchBlobAssetsInChunkAfterLoad(Chunk* chunk, byte* allBlobA continue; var blobAssetRefOffsets = TypeManager.GetBlobAssetRefOffsets(ct); - var chunkBuffer = chunk->Buffer; int subArrayOffset = archetype->Offsets[ti]; byte* componentArrayStart = OffsetFromPointer(chunkBuffer, subArrayOffset); @@ -663,15 +678,16 @@ public static Entity GetSceneSectionEntity(int sectionIndex, EntityManager manag internal static Hash128 BufferDataNodeType = new Hash128("E33124BFAC2649D792DE36E4611DDD70"); internal static Hash128 PrefabNodeType = new Hash128("2A84A183583A4FAD8CB7105AE3C47598"); - internal unsafe struct MegaChunkInfo + internal struct MegaChunkInfo { - public byte* MegaChunkAddress; public int MegaChunkSize; + public ChunkIndex MegaChunkIndex; - public MegaChunkInfo(byte* chunks, int size) + public MegaChunkInfo(ChunkIndex fistChunkIndex, int chunkCount) { - MegaChunkAddress = chunks; - MegaChunkSize = size; + Assert.IsTrue(chunkCount > 0); + MegaChunkSize = chunkCount; + MegaChunkIndex = fistChunkIndex; } } @@ -874,7 +890,7 @@ internal static unsafe void EndDeserializeWorld(ExclusiveEntityTransaction manag // Note, do not move this code to "using (var sharedComponentReader = status.SharedComponentPrefetchState.CreateStream()) {}" // until UUM-27771 is resolved. var sharedComponentReader = status.SharedComponentPrefetchState.CreateStream(); - + // read the full index list int sharedComponentArraysLength = sharedComponentReader.ReadInt(); sharedComponentArray = new NativeArray(sharedComponentArraysLength, Allocator.Temp); @@ -884,7 +900,8 @@ internal static unsafe void EndDeserializeWorld(ExclusiveEntityTransaction manag // Also see below the offset + 1 indices for the same reason sharedComponentRemap.Add(0); - var unmanagedSharedComponentCount = ReadUnmanagedSharedComponents(manager, sharedComponentReader, sharedComponentRemap); + var unmanagedSharedComponentCount = ReadUnmanagedSharedComponents(manager, sharedComponentReader, sharedComponentRemap, + (WorldDeserializationStatus*)UnsafeUtility.AddressOf(ref status)); var managedSharedComponentCount = ReadManagedSharedComponents(manager, sharedComponentReader, sharedComponentRemap, unityObjects, blobAssetBuffer); numSharedComponents = unmanagedSharedComponentCount + managedSharedComponentCount; @@ -923,7 +940,7 @@ internal static unsafe void EndDeserializeWorld(ExclusiveEntityTransaction manag for (int i = 0; i < chunksWithMetaChunkEntities.Length; ++i) { var chunkw = chunksWithMetaChunkEntities[i].m_Chunk; - manager.SetComponentData(chunkw->metaChunkEntity, new ChunkHeader {ArchetypeChunk = chunksWithMetaChunkEntities[i]}); + manager.SetComponentData(chunkw.MetaChunkEntity, new ChunkHeader { ArchetypeChunk = chunksWithMetaChunkEntities[i] }); } chunksWithMetaChunkEntities.Dispose(); archetypes.Dispose(); @@ -974,7 +991,6 @@ public static unsafe void DeserializeWorld(ExclusiveEntityTransaction manager, B internal static unsafe void DeserializeWorldInternal(ExclusiveEntityTransaction manager, BinaryReader reader, out WorldDeserializationResult deserializationResult, object[] unityObjects = null) { -#if !UNITY_DOTSRUNTIME if (reader is StreamBinaryReader) { using (var dotsReader = DotsSerialization.CreateReader(reader)) @@ -987,7 +1003,7 @@ internal static unsafe void DeserializeWorldInternal(ExclusiveEntityTransaction return; } } -#endif + using(var dotsReader = DotsSerialization.CreateReader(reader)) { var readCommands = new UnsafeList(1, Allocator.Temp); @@ -1002,12 +1018,12 @@ internal static unsafe void DeserializeWorldInternal(ExclusiveEntityTransaction } internal static void SerializeWorldInternal(EntityManager entityManager, BinaryWriter writer, out object[] referencedObjects, - NativeArray entityRemapInfos, bool isDOTSRuntime = false) + NativeArray entityRemapInfos) { - SerializeWorldInternal(entityManager, writer, out referencedObjects, entityRemapInfos, default, new Settings(), isDOTSRuntime); + SerializeWorldInternal(entityManager, writer, out referencedObjects, entityRemapInfos, default, new Settings()); } - internal static unsafe BlobAssetReference SerializeWorldInternal(EntityManager entityManager, BinaryWriter writer, out object[] referencedObjects, NativeArray entityRemapInfos, NativeParallelHashSet weakAssetRefs, Settings settings, bool isDOTSRuntime = false, bool buildBlobHeader = false) + internal static unsafe BlobAssetReference SerializeWorldInternal(EntityManager entityManager, BinaryWriter writer, out object[] referencedObjects, NativeArray entityRemapInfos, NativeParallelHashSet weakAssetRefs, Settings settings, bool buildBlobHeader = false) { BlobAssetReference blobHeader = default; using (var dotsWriter = DotsSerialization.CreateWriter(writer, WorldFileType, "EntityBinaryFile")) @@ -1173,32 +1189,35 @@ private static unsafe void WriteChunks(NativeArrayChunks[ci]; - UnsafeUtility.MemCpy(tempChunk, chunk, Chunk.kChunkSize); - tempChunk->ChunkstoreIndex = 0; + UnsafeUtility.MemCpy(tempChunk, chunk.GetPtr(), Chunk.kChunkSize); + + var tempChunkBuffer = tempChunk->Buffer; + var entityCount = chunk.Count; + + tempChunk->CountForSerialization = entityCount; tempChunk->metaChunkEntity = EntityRemapUtility.RemapEntity(ref entityRemapInfos, tempChunk->metaChunkEntity); // Prevent patching from touching buffers allocated memory - BufferHeader.PatchAfterCloningChunk(tempChunk); - PatchManagedComponentIndices(tempChunk, archetype, ref currentManagedComponentIndex, mcs); + BufferHeader.PatchAfterCloningChunk(archetype, tempChunkBuffer, entityCount); + PatchManagedComponentIndices(tempChunkBuffer, entityCount, archetype, ref currentManagedComponentIndex, mcs); - byte* tempChunkBuffer = tempChunk->Buffer; EntityRemapUtility.PatchEntities(archetype->ScalarEntityPatches, archetype->ScalarEntityPatchCount, archetype->BufferEntityPatches, archetype->BufferEntityPatchCount, - tempChunkBuffer, tempChunk->Count, ref entityRemapInfos); + tempChunkBuffer, entityCount, ref entityRemapInfos); if (archetype->HasBlobAssetRefs) - PatchBlobAssetsInChunkBeforeSave(tempChunk, chunk, blobAssetOffsets, blobAssetMap); + PatchBlobAssetsInChunkBeforeSave(archetype, tempChunkBuffer, entityCount, blobAssetOffsets, blobAssetMap); if (archetype->HasWeakAssetRefs) - GetWeakAssetRefsInChunk(tempChunk, weakAssetRefs); + GetWeakAssetRefsInChunk(archetype, tempChunkBuffer, entityCount, weakAssetRefs); - ClearChunkHeaderComponents(tempChunk); - ChunkDataUtility.MemsetUnusedChunkData(tempChunk, 0); + ClearChunkHeaderComponents(archetype, tempChunkBuffer, entityCount); + ChunkDataUtility.MemsetUnusedChunkData(archetype, tempChunkBuffer, 0, entityCount); var startPatchesIndex = bufferPatches.Length; - FillBufferPatchRecordsAndClearBufferPointer(tempChunk, bufferPatches, bufferDataList); + FillBufferPatchRecordsAndClearBufferPointer(archetype, tempChunkBuffer, entityCount, bufferPatches, bufferDataList); var recordCount = bufferPatches.Length - startPatchesIndex; bufferPatchesCountPerChunk.Add(recordCount); - tempChunk->Archetype = (Archetype*) a; + tempChunk->ArchetypeIndexForSerialization = a; w.WriteBytes(tempChunk, Chunk.kChunkSize); } @@ -1491,13 +1510,15 @@ static int Align16(int x) return (x + 15) & ~15; } - unsafe static void PatchManagedComponentIndices(Chunk* chunk, Archetype* archetype, ref int currentManagedIndex, ManagedComponentStore managedComponentStore) + static unsafe void PatchManagedComponentIndices(byte* chunkBuffer, int entityCount, Archetype* archetype, ref int currentManagedIndex, ManagedComponentStore managedComponentStore) { for (int i = 0; i < archetype->NumManagedComponents; ++i) { - var index = archetype->TypeMemoryOrderIndexToIndexInArchetype[i + archetype->FirstManagedComponent]; - var managedComponentIndices = (int*)ChunkDataUtility.GetComponentDataRO(chunk, 0, index); - for (int ei = 0; ei < chunk->Count; ++ei) + var indexInTypeArray = archetype->TypeMemoryOrderIndexToIndexInArchetype[i + archetype->FirstManagedComponent]; + var offset = archetype->Offsets[indexInTypeArray]; + var managedComponentIndices = (int*)(chunkBuffer + offset); + + for (int ei = 0; ei < entityCount; ++ei) { if (managedComponentIndices[ei] == 0) continue; @@ -1573,10 +1594,10 @@ static unsafe void WriteSharedAndManagedComponents( for (int i = 0; i < archetype->NumManagedComponents; ++i) { var index = archetype->TypeMemoryOrderIndexToIndexInArchetype[i + archetype->FirstManagedComponent]; - var managedComponentIndices = (int*)ChunkDataUtility.GetComponentDataRO(chunk, 0, index); + var managedComponentIndices = (int*)ChunkDataUtility.GetComponentDataRO(chunk, archetype, 0, index); ref readonly var cType = ref TypeManager.GetTypeInfo(archetype->Types[index].TypeIndex); - for (int ei = 0; ei < chunk->Count; ++ei) + for (int ei = 0, count = chunk.Count; ei < count; ++ei) { if (managedComponentIndices[ei] == 0) continue; @@ -1681,8 +1702,11 @@ static unsafe SharedComponentRecord WriteUnmanagedSharedComponent( return record; } - static unsafe int ReadUnmanagedSharedComponents(ExclusiveEntityTransaction manager, BinaryReader reader, NativeList sharedComponentRemap) + static unsafe int ReadUnmanagedSharedComponents(ExclusiveEntityTransaction manager, BinaryReader reader, NativeList sharedComponentRemap, + SerializeUtility.WorldDeserializationStatus* status) { + byte* blobAssetBuffer = (byte*)status->BlobAssetBuffer; + var unmanagedSharedComponentCount = ReadSharedComponentMetadata(reader, out var sharedComponentRecordArray); int dataSize = reader.ReadInt(); @@ -1699,7 +1723,28 @@ static unsafe int ReadUnmanagedSharedComponents(ExclusiveEntityTransaction manag var typeIndex = TypeManager.GetTypeIndexFromStableTypeHash(record.StableTypeHash); int sharedComponentIndex; { - void* data = unmanagedStream.ReadNext(record.ComponentSize); + byte* data = (byte*)unmanagedStream.ReadNext(record.ComponentSize); + + var ct = TypeManager.GetTypeInfo(typeIndex); + var blobAssetRefCount = ct.BlobAssetRefOffsetCount; + if (blobAssetRefCount > 0) + { + var blobAssetRefOffsets = TypeManager.GetBlobAssetRefOffsets(ct); + + // We patch the blob asset ref + for (int blobIndex = 0; blobIndex < blobAssetRefCount; ++blobIndex) + { + var offset = blobAssetRefOffsets[blobIndex].Offset; + var blobAssetRefPtr = (BlobAssetReferenceData*)(data + offset); + int value = (int)blobAssetRefPtr->m_Ptr; + byte* ptr = null; + if (value != -1) + { + ptr = blobAssetBuffer + value; + } + blobAssetRefPtr->m_Ptr = ptr; + } + } var hashCode = TypeManager.GetHashCode(data, typeIndex); sharedComponentIndex = access->EntityComponentStore->InsertSharedComponent_Unmanaged(typeIndex, hashCode, data, null); @@ -1768,8 +1813,6 @@ static unsafe void ReadSharedComponents(ExclusiveEntityTransaction manager, ref } } -#if !UNITY_DOTSRUNTIME - // True when a component is valid to using in world serialization. A component IsSerializable when it is valid to blit // the data across storage media. Thus components containing pointers have an IsSerializable of false as the component // is blittable but no longer valid upon deserialization. @@ -1800,7 +1843,6 @@ private static bool IsTypeValidForSerialization(Type type, HashSet validTy return true; } -#endif // !UNITY_DOTSRUNTIME [BurstDiscard] private static void ValidateTypeForSerialization(TypeManager.TypeInfo typeInfo) @@ -1853,7 +1895,7 @@ static unsafe void GatherAllUsedBlobAssets( for (var ci = 0; ci < archetype->Chunks.Count; ++ci) { var chunk = archetype->Chunks[ci]; - var entityCount = chunk->Count; + var entityCount = chunk.Count; for (var unordered_ti = 0; unordered_ti < typeCount; ++unordered_ti) { var ti = archetype->TypeMemoryOrderIndexToIndexInArchetype[unordered_ti]; @@ -1867,7 +1909,7 @@ static unsafe void GatherAllUsedBlobAssets( continue; var blobAssetRefOffsets = TypeManager.GetBlobAssetRefOffsets(ct); - var chunkBuffer = chunk->Buffer; + var chunkBuffer = chunk.Buffer; if (blobAssetRefCount > 0) { @@ -1904,7 +1946,7 @@ static unsafe void GatherAllUsedBlobAssets( } } - var sharedComponentValues = chunk->SharedComponentValues; + var sharedComponentValues = archetype->Chunks.GetSharedComponentValues(chunk.ListIndex); for (var i = 0; i < archetype->NumSharedComponents; i++) { var sharedComponentIndex = sharedComponentValues[i]; @@ -1957,10 +1999,10 @@ static unsafe void GatherAllUsedBlobAssets( for (var unorderedTypeIndexInArchetype = 0; unorderedTypeIndexInArchetype < archetype->NumManagedComponents; ++unorderedTypeIndexInArchetype) { var typeIndexInArchetype = archetype->TypeMemoryOrderIndexToIndexInArchetype[archetype->FirstManagedComponent + unorderedTypeIndexInArchetype]; - var managedComponentIndices = (int*) ChunkDataUtility.GetComponentDataRO(chunk, 0, typeIndexInArchetype); + var managedComponentIndices = (int*) ChunkDataUtility.GetComponentDataRO(chunk, archetype, 0, typeIndexInArchetype); ref readonly var typeInfo = ref TypeManager.GetTypeInfo(archetype->Types[typeIndexInArchetype].TypeIndex); - for (var entityIndex = 0; entityIndex < chunk->Count; entityIndex++) + for (int entityIndex = 0, count = chunk.Count; entityIndex < count; entityIndex++) { if (managedComponentIndices[entityIndex] == 0) continue; @@ -2003,11 +2045,9 @@ private static unsafe void AddBlobAssetRefInfo(byte* componentData, TypeManager. } } - private static unsafe void GetWeakAssetRefsInChunk(Chunk* chunk, NativeParallelHashSet weakAssetRefs) + private static unsafe void GetWeakAssetRefsInChunk(Archetype* archetype, byte* chunkBuffer, int entityCount, NativeParallelHashSet weakAssetRefs) { - var archetype = chunk->Archetype; var typeCount = archetype->TypesCount; - var entityCount = chunk->Count; for (var unordered_ti = 0; unordered_ti < typeCount; ++unordered_ti) { var ti = archetype->TypeMemoryOrderIndexToIndexInArchetype[unordered_ti]; @@ -2022,7 +2062,6 @@ private static unsafe void GetWeakAssetRefsInChunk(Chunk* chunk, NativeParallelH continue; var weakAssetRefOffsets = TypeManager.GetWeakAssetRefOffsets(ct); - var chunkBuffer = chunk->Buffer; int subArrayOffset = archetype->Offsets[ti]; byte* componentArrayStart = OffsetFromPointer(chunkBuffer, subArrayOffset); @@ -2066,12 +2105,10 @@ private static unsafe void GetWeakAssetRefsInComponent(byte* componentData, Type } } - private static unsafe void PatchBlobAssetsInChunkBeforeSave(Chunk* tempChunk, Chunk* originalChunk, + private static unsafe void PatchBlobAssetsInChunkBeforeSave(Archetype* archetype, byte* chunkBuffer, int entityCount, NativeArray blobAssetOffsets, NativeParallelHashMap blobAssetMap) { - var archetype = originalChunk->Archetype; var typeCount = archetype->TypesCount; - var entityCount = originalChunk->Count; for (var unordered_ti = 0; unordered_ti < typeCount; ++unordered_ti) { var ti = archetype->TypeMemoryOrderIndexToIndexInArchetype[unordered_ti]; @@ -2085,7 +2122,6 @@ private static unsafe void PatchBlobAssetsInChunkBeforeSave(Chunk* tempChunk, Ch continue; var blobAssetRefOffsets = TypeManager.GetBlobAssetRefOffsets(ct); - var chunkBuffer = tempChunk->Buffer; int subArrayOffset = archetype->Offsets[ti]; byte* componentArrayStart = OffsetFromPointer(chunkBuffer, subArrayOffset); @@ -2136,17 +2172,13 @@ private static unsafe void PatchBlobAssetRefInfoBeforeSave(byte* componentData, } } - private static unsafe void FillBufferPatchRecordsAndClearBufferPointer(Chunk* chunk, NativeList bufferPatches, NativeList bufferPtrs) + private static unsafe void FillBufferPatchRecordsAndClearBufferPointer(Archetype* archetype, byte* chunkBuffer, int entityCount, NativeList bufferPatches, NativeList bufferPtrs) { - byte* tempChunkBuffer = chunk->Buffer; - int entityCount = chunk->Count; - Archetype* archetype = chunk->Archetype; - // Find all buffer pointer locations and work out how much memory the deserializer must allocate on load. for (int ti = 0; ti < archetype->TypesCount; ++ti) { - int index = archetype->TypeMemoryOrderIndexToIndexInArchetype[ti]; - var type = archetype->Types[index]; + int indexInTypeArray = archetype->TypeMemoryOrderIndexToIndexInArchetype[ti]; + var type = archetype->Types[indexInTypeArray]; if (type.IsBuffer) { @@ -2157,14 +2189,17 @@ private static unsafe void FillBufferPatchRecordsAndClearBufferPointer(Chunk* ch for (int bi = 0; bi < entityCount; ++bi) { - var header = (BufferHeader*)ChunkDataUtility.GetComponentDataRO(chunk, bi, index); + var offset = archetype->Offsets[indexInTypeArray]; + var sizeOf = archetype->SizeOfs[indexInTypeArray]; + + var header = (BufferHeader*)(chunkBuffer + (offset + sizeOf * bi)); if (header->Pointer != null) { int capacityInBytes = elementSize * header->Capacity; bufferPatches.Add(new BufferPatchRecord { - ChunkOffset = (int)(((byte*)header) - tempChunkBuffer), + ChunkOffset = (int)((byte*)header - chunkBuffer), AllocSizeBytes = capacityInBytes }); bufferPtrs.Add((IntPtr)header->Pointer); @@ -2198,9 +2233,10 @@ private static unsafe void FillSharedComponentArrays(NativeArray sharedComp continue; FillSharedComponentIndexRemap(sharedComponentIndexRemap, archetype); - for (int iChunk = 0; iChunk < archetype->Chunks.Count; ++iChunk) + var archetypeChunkData = archetype->Chunks; + for (int iChunk = 0; iChunk < archetypeChunkData.Count; ++iChunk) { - var sharedComponents = archetype->Chunks[iChunk]->SharedComponentValues; + var sharedComponents = archetypeChunkData.GetSharedComponentValues(archetypeChunkData[iChunk].ListIndex); for (int iType = 0; iType < numSharedComponents; iType++) { int remappedIndex = sharedComponentIndexRemap[iType]; @@ -2240,19 +2276,16 @@ private static unsafe NativeParallelHashMap GatherSharedComponents(Uns return sharedIndexToSerialize; } - private static unsafe void ClearChunkHeaderComponents(Chunk* chunk) + private static unsafe void ClearChunkHeaderComponents(Archetype* archetype, byte* chunkBuffer, int entityCount) { var chunkHeaderTypeIndex = TypeManager.GetTypeIndex(); - var archetype = chunk->Archetype; - var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(chunk->Archetype, chunkHeaderTypeIndex); + var typeIndexInArchetype = ChunkDataUtility.GetIndexInTypeArray(archetype, chunkHeaderTypeIndex); if (typeIndexInArchetype == -1) return; - var buffer = chunk->Buffer; - var length = chunk->Count; var startOffset = archetype->Offsets[typeIndexInArchetype]; - var chunkHeaders = (ChunkHeader*)(buffer + startOffset); - for (int i = 0; i < length; ++i) + var chunkHeaders = (ChunkHeader*)(chunkBuffer + startOffset); + for (int i = 0; i < entityCount; ++i) { chunkHeaders[i] = ChunkHeader.Null; } @@ -2295,9 +2328,9 @@ static unsafe int GenerateRemapInfo(EntityManager entityManager, UnsafePtrListChunks.Count; ++i) { var chunk = archetype->Chunks[i]; - for (int iEntity = 0; iEntity < chunk->Count; ++iEntity) + for (int iEntity = 0, count = chunk.Count; iEntity < count; ++iEntity) { - var entity = *(Entity*)ChunkDataUtility.GetComponentDataRO(chunk, iEntity, 0); + var entity = *(Entity*)ChunkDataUtility.GetComponentDataRO(chunk, archetype, iEntity, 0); EntityRemapUtility.AddEntityRemapping(ref entityRemapInfos, entity, new Entity { Version = 1, Index = nextEntityId }); ++nextEntityId; } diff --git a/Unity.Entities/Serialization/SerializeUtilityToYaml.cs b/Unity.Entities/Serialization/SerializeUtilityToYaml.cs deleted file mode 100644 index 3de7836..0000000 --- a/Unity.Entities/Serialization/SerializeUtilityToYaml.cs +++ /dev/null @@ -1,417 +0,0 @@ -#if UNITY_EDITOR -using Unity.Collections; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Runtime.InteropServices; -using Unity.Assertions; -using Unity.Collections.LowLevel.Unsafe; -using Unity.Serialization.Json; - -namespace Unity.Entities.Serialization -{ - public static partial class SerializeUtility - { - #region World Yaml Serialization - - /// - /// Serialize the given World to a YAML file, for logging and analysis purpose - /// - /// Entity Manager of the World to serialize - /// The stream we will write the data to - /// If true the binary data of each chunk section (header, component data of each entity) will be saved. - /// This will increase the volume of data being written drastically, but you will be able to diff at the binary level - /// - /// - /// Analysing a serialized sub-scene for instance is not easy because the data is raw binary. It is even harder if we want to compare two distinct serialization of the same sub-scene. - /// This method will allow us to save the data of a given World in YAML. - /// Note that so far the data being saved is not totally complete, it will improve over time. - /// - public static unsafe void SerializeWorldIntoYAML(EntityManager entityManager, StreamWriter writer, bool dumpChunkRawData) - { - if (writer.NewLine != "\n") - { - throw new ArgumentException("YAML World serialization must be done with a line ending being \\n on all platforms", nameof(writer)); - } - - var yaml = new YamlWriter(writer); - WriteYAMLHeader(yaml); - WriteArchetypes(yaml, entityManager); - - var access = entityManager.GetCheckedEntityDataAccess(); - var entityComponentStore = access->EntityComponentStore; - - using (var archetypeArray = GetAllArchetypes(entityComponentStore, Allocator.Temp)) - using (var entityRemapInfos = new NativeArray(entityManager.EntityCapacity, Allocator.Temp)) - using (yaml.WriteCollection(k_ChunksDataCollectionTag)) - { - GenerateRemapInfo(entityManager, archetypeArray, entityRemapInfos); - for (int a = 0; a < archetypeArray.Length; ++a) - { - var archetype = archetypeArray.Ptr[a]; - using (yaml.WriteCollection(k_ArchetypeCollectionTag)) - { - yaml.WriteKeyValue("name", archetype->ToString()); - - for (var ci = 0; ci < archetype->Chunks.Count; ++ci) - { - var chunk = archetype->Chunks[ci]; - WriteChunkData(yaml, entityManager, entityRemapInfos, chunk, archetype, a, dumpChunkRawData); - } - } - } - } - } - - #region Tags - - const string k_ArchetypesCollectionTag = "Archetypes"; - const string k_ArchetypeCollectionTag = "Archetype"; - const string k_ChunksDataCollectionTag = "ChunksData"; - const string k_ChunkDataCollectionTag = "ChunkData"; - const string k_ComponentDataCollectionTag = "ComponentDataCollection"; - const string k_ComponentDataTag = "ComponentData"; - - #endregion - - #region Data type field info extraction - - /// - /// Helper class that will build for a given type a list of all its data fields with the required information for us to dump these fields data later on - /// - internal static class TypeDataExtractor - { - [DebuggerDisplay("Type = {" + nameof(Type) + "}")] - [DebuggerTypeProxy(typeof(TypeExtractedInfoDebugView))] - public class TypeExtractedInfo - { - public Type Type { get; } - public FieldExtractedInfo[] Fields { get; } - - public TypeExtractedInfo(Type type) - { - Type = type; - Fields = type.GetFields().Select(field => new FieldExtractedInfo(field.Name, field.FieldType, UnsafeUtility.GetFieldOffset(field))).OrderBy(field => field.Name).ToArray(); - } - } - - sealed class TypeExtractedInfoDebugView - { - TypeExtractedInfo Owner; - - public TypeExtractedInfoDebugView(TypeExtractedInfo obj) - { - Owner = obj; - } - - [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)] - public FieldExtractedInfo[] Items => Owner.Fields; - } - - [DebuggerDisplay("Name = {Name}, Offset = {Offset}, Type = {Type}")] - public class FieldExtractedInfo - { - public FieldExtractedInfo(string name, Type type, int offset) - { - Name = name; - Type = type; - Offset = offset; - Size = UnsafeUtility.SizeOf(Type); - } - - public string Name { get; } - public Type Type { get; } - public int Offset { get; } - public int Size { get; } - } - - public static TypeExtractedInfo GetTypeExtractedInfo(Type type) => Types.GetOrAdd(type, t => new TypeExtractedInfo(t)); - - static ConcurrentDictionary Types = new ConcurrentDictionary(); - } - - #endregion - - #region Internal implementation - - struct EntityInfo - { - public EntityInfo(Entity entity, EntityInChunk entityInChunk) - { - Entity = entity; - EntityInChunk = entityInChunk; - } - - public Entity Entity; - public EntityInChunk EntityInChunk; - } - - static void WriteYAMLHeader(YamlWriter writer) - { - if (writer.CurrentIndent != 0) - { - throw new InvalidOperationException("The header can only be written as root element"); - } - - writer.WriteLine(@"%YAML 1.1") - .WriteLine(@"---") - .WriteLine(@"# ECS Debugging file"); - } - - static unsafe void WriteArchetypes(YamlWriter writer, EntityManager entityManager) - { - var access = entityManager.GetCheckedEntityDataAccess(); - var entityComponentStore = access->EntityComponentStore; - - using (var archetypeArray = GetAllArchetypes(entityComponentStore, Allocator.Temp)) - using (writer.WriteCollection(k_ArchetypesCollectionTag)) - { - for (int i = 0; i != archetypeArray.Length; i++) - { - var a = archetypeArray.Ptr[i]; - using (writer.WriteCollection(k_ArchetypeCollectionTag)) - { - writer.WriteKeyValue("name", a->ToString()) - .WriteKeyValue(nameof(Archetype.TypesCount), a->TypesCount) - .WriteKeyValue(nameof(EntityArchetype.ChunkCount), a->Chunks.Count) - .WriteKeyValue(nameof(EntityArchetype.ChunkCapacity), a->ChunkCapacity); - - var props = new List(); - if (a->CleanupComplete) props.Add(nameof(Archetype.CleanupComplete)); - if (a->CleanupNeeded) props.Add(nameof(Archetype.CleanupNeeded)); - if (a->Disabled) props.Add(nameof(Archetype.Disabled)); - if (a->Prefab) props.Add(nameof(Archetype.Prefab)); - if (a->HasChunkComponents) props.Add(nameof(Archetype.HasChunkComponents)); - if (a->HasChunkHeader) props.Add(nameof(Archetype.HasChunkHeader)); - if (a->HasBlobAssetRefs) props.Add(nameof(Archetype.HasBlobAssetRefs)); - writer.WriteInlineSequence("properties", props); - } - } - } - } - - static unsafe void WriteChunkData(YamlWriter writer, EntityManager entityManager, NativeArray entityRemapInfos, Chunk* initialChunk, Archetype* archetype, int archetypeIndex, bool dumpChunkRawData) - { - var tempChunkMem = stackalloc byte[Chunk.kChunkSize]; - Chunk* tempChunk = (Chunk*)tempChunkMem; - if (dumpChunkRawData) - { - UnsafeUtility.MemCpy(tempChunk, initialChunk, Chunk.kChunkSize); - tempChunk->ChunkstoreIndex = 0; - - byte* tempChunkBuffer = tempChunk->Buffer; - - BufferHeader.PatchAfterCloningChunk(tempChunk); - EntityRemapUtility.PatchEntities(archetype->ScalarEntityPatches, archetype->ScalarEntityPatchCount, archetype->BufferEntityPatches, archetype->BufferEntityPatchCount, tempChunkBuffer, tempChunk->Count, ref entityRemapInfos); - ClearChunkHeaderComponents(tempChunk); - ChunkDataUtility.MemsetUnusedChunkData(tempChunk, 0); - - tempChunk->Archetype = (Archetype*)archetypeIndex; - } - - using (writer.WriteCollection(k_ChunkDataCollectionTag)) - { - using (writer.WriteCollection("Header")) - { - WriteEntity(writer, nameof(Chunk.metaChunkEntity), initialChunk->metaChunkEntity); - writer.WriteKeyValue(nameof(Chunk.Capacity), initialChunk->Capacity); - writer.WriteKeyValue(nameof(Chunk.Count), initialChunk->Count); - - if (dumpChunkRawData) - { - writer.WriteFormattedBinaryData("Header-RawData", tempChunk, Chunk.kBufferOffset); - } - } - - // First pass to sort by component type - var entitiesByChunkIndex = new Entity[initialChunk->Count]; - var componentDataList = new List(); - var chunkComponentDataList = new List(); - var chunkTypes = archetype->Types; - for (int typeI = 0; typeI < archetype->TypesCount; typeI++) - { - var componentType = &chunkTypes[typeI]; - var type = TypeManager.GetType(componentType->TypeIndex); - ref readonly var typeInfo = ref TypeManager.GetTypeInfo(componentType->TypeIndex); - - if (componentType->IsChunkComponent) - { - chunkComponentDataList.Add(typeI); - } - // Is it a Component Data ? - else if (typeof(IComponentData).IsAssignableFrom(type) || typeof(Entity).IsAssignableFrom(type) || typeof(IBufferElementData).IsAssignableFrom(type)) - { - // Ignore Tag Component, no data to dump - if (typeInfo.IsZeroSized) continue; - - if (typeof(Entity).IsAssignableFrom(type)) - { - componentDataList.Insert(0, typeI); - - for (int i = 0; i < initialChunk->Count;) - { - var entity = *(Entity*)(initialChunk->Buffer + archetype->SizeOfs[0] * i); - Assert.IsTrue(entityManager.Exists(entity)); - - entitiesByChunkIndex[i] = entity; - i++; - } - } - else - { - componentDataList.Add(typeI); - } - } - } - - // Parse the Component Data for this chunk and store them - using (writer.WriteCollection(k_ComponentDataCollectionTag)) - { - var access = entityManager.GetCheckedEntityDataAccess(); - foreach (var typeI in componentDataList) - { - var componentTypeInArchetype = &chunkTypes[typeI]; - var componentType = TypeManager.GetType(componentTypeInArchetype->TypeIndex); - - ref readonly var componentTypeInfo = ref TypeManager.GetTypeInfo(componentTypeInArchetype->TypeIndex); - TypeDataExtractor.TypeExtractedInfo componentExtractedInfo = null; - if (UnsafeUtility.IsUnmanaged(componentType)) - componentExtractedInfo = TypeDataExtractor.GetTypeExtractedInfo(componentType); - - using (writer.WriteCollection(k_ComponentDataTag)) - { - writer.WriteInlineMap("info", new[] - { - new System.Collections.Generic.KeyValuePair(nameof(System.Type), componentType.Name), - new System.Collections.Generic.KeyValuePair(nameof(TypeManager.TypeInfo.SizeInChunk), componentTypeInfo.SizeInChunk) - }); - - using (writer.WriteCollection("Entities")) - { - var indexInTypeArray = ChunkDataUtility.GetIndexInTypeArray(archetype, componentTypeInArchetype->TypeIndex); - var componentOffsetInChunk = archetype->Offsets[indexInTypeArray]; - var componentSize = archetype->SizeOfs[indexInTypeArray]; - var componentsBuffer = initialChunk->Buffer + componentOffsetInChunk; - var entityData = new Dictionary(); - - // Dump all entities in this chunk - for (int i = 0; i < entitiesByChunkIndex.Length; i++) - { - var entity = entitiesByChunkIndex[i]; - entityData.Clear(); - - // Get the location of the component data - var compData = componentsBuffer + i * componentSize; - - // If the component we are dumping is a Dynamic Buffer - if (typeof(IBufferElementData).IsAssignableFrom(componentType)) - { - var header = (BufferHeader*)compData; - var begin = BufferHeader.GetElementPointer(header); - var size = componentTypeInfo.ElementSize; - - using (writer.WriteCollection(entity.ToString())) - { - writer.WriteLine($"Length: {header->Length} Capacity: {header->Capacity}"); - - for (var it = 0; it < header->Length; it++) - { - var item = begin + (size * it); - entityData.Clear(); - - // Dump each field of the current entity's component data - foreach (var componentFieldInfo in componentExtractedInfo.Fields) - { - var compDataObject = Marshal.PtrToStructure((IntPtr)item + componentFieldInfo.Offset, componentFieldInfo.Type); - entityData.Add(componentFieldInfo.Name, compDataObject.ToString()); - } - writer.WriteInlineMap($"{it:0000}", entityData); - } - } - - if (dumpChunkRawData) - { - if (componentSize > 0) - writer.WriteFormattedBinaryData("ComponentRawData", begin, size * header->Length, 0); - } - } - else - { - // If it's a Component Data - if (componentTypeInfo.Category == TypeManager.TypeCategory.ComponentData && !componentTypeInfo.Type.IsClass || componentTypeInfo.Category == TypeManager.TypeCategory.EntityData) - { - // Dump each field of the current entity's component data - foreach (var componentFieldInfo in componentExtractedInfo.Fields) - { - var compDataObject = Marshal.PtrToStructure((IntPtr)compData + componentFieldInfo.Offset, componentFieldInfo.Type); - entityData.Add(componentFieldInfo.Name, compDataObject.ToString()); - } - writer.WriteInlineMap(entity.ToString(), entityData); - } - else if (componentTypeInfo.Category == TypeManager.TypeCategory.ComponentData && componentTypeInfo.Type.IsClass) - { - var obj = access->ManagedComponentStore.GetManagedComponent(*(int*)(compData)); - if (obj == null) - writer.WriteLine("null"); - else - writer.WriteLine(JsonSerialization.ToJson(obj)); - } - //@Todo: Shared components - - if (dumpChunkRawData) - { - if (componentSize > 0) - writer.WriteFormattedBinaryData("ComponentRawData", compData, componentSize, 0); - } - } - } - } - } - } - - if (dumpChunkRawData) - { - writer.WriteLine("Archetype: " + initialChunk->Archetype->ToString()); - - using (var bufferPatches = new NativeList(128, Allocator.Temp)) - using (var bufferPtrs = new NativeList(128, Allocator.Temp)) - { - tempChunk->Archetype = archetype; - FillBufferPatchRecordsAndClearBufferPointer(tempChunk, bufferPatches, bufferPtrs); - for (int i = 0; i < bufferPtrs.Length; ++i) - { - var ptr = (void*)bufferPtrs[i]; - BufferHeader.FreeBufferPtr(ptr); - } - } - - for (int i = 0; i != tempChunk->Archetype->TypesCount; i++) - { - var begin = archetype->Offsets[i]; - var end = i == tempChunk->Archetype->TypesCount -1 ? Chunk.GetChunkBufferSize() : archetype->Offsets[i+1]; - writer.WriteLine("Type: " + tempChunk->Archetype->Types[i]); - writer.WriteFormattedBinaryData("Chunk Row", tempChunk->Buffer + begin, end - begin, 0); - } - - //writer.WriteLine("Archetype: " + initialChunk->Archetype->ToString()); - //writer.WriteFormattedBinaryData("Complete chunk", tempChunk, Chunk.kChunkSize, 0); - } - } - } - } - - static void WriteEntity(YamlWriter writer, string name, Entity entity) - { - writer.WriteInlineMap(name, new[] { new System.Collections.Generic.KeyValuePair("index", entity.Index), new System.Collections.Generic.KeyValuePair("version", entity.Version) }); - } - - #endregion - - #endregion - } -} - -#endif //UNITY_EDITOR diff --git a/Unity.Entities/Serialization/SerializeUtilityToYaml.cs.meta b/Unity.Entities/Serialization/SerializeUtilityToYaml.cs.meta deleted file mode 100644 index 8309e3d..0000000 --- a/Unity.Entities/Serialization/SerializeUtilityToYaml.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 2334573748ece78449c6f060c0a0a424 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities/Serialization/YamlWriter.cs b/Unity.Entities/Serialization/YamlWriter.cs index c54d783..7c13fb6 100644 --- a/Unity.Entities/Serialization/YamlWriter.cs +++ b/Unity.Entities/Serialization/YamlWriter.cs @@ -1,4 +1,3 @@ -#if !NET_DOTS using System; using System.Collections.Generic; using System.IO; @@ -275,4 +274,3 @@ public Collection(YamlWriter owner, string collectionName) : base(owner, true) #endregion } } -#endif diff --git a/Unity.Entities/SourceGenerators/Source~/AspectGenerator/AspectGenerator.cs b/Unity.Entities/SourceGenerators/Source~/AspectGenerator/AspectGenerator.cs index f9a78b8..5a1d029 100644 --- a/Unity.Entities/SourceGenerators/Source~/AspectGenerator/AspectGenerator.cs +++ b/Unity.Entities/SourceGenerators/Source~/AspectGenerator/AspectGenerator.cs @@ -470,7 +470,7 @@ SemanticModel GetSemanticModel(SyntaxNode syntaxNode) var code = syntaxTreeSourceBuilder.ToString(); SourceOutputHelpers.OutputSourceToFile( - syntaxTree.GetGeneratedSourceFilePath(context.Compilation.Assembly.Name, s_GeneratorName), + syntaxTree.GetGeneratedSourceFilePath(context.Compilation.Assembly.Name, s_GeneratorName).FullFilePath, () => code); var generatedSourceHint = syntaxTree.GetGeneratedSourceFileName(s_GeneratorName); diff --git a/Unity.Entities/SourceGenerators/Source~/AspectGenerator/AspectSyntaxFactory.cs b/Unity.Entities/SourceGenerators/Source~/AspectGenerator/AspectSyntaxFactory.cs index 73a7a66..dd36ef5 100644 --- a/Unity.Entities/SourceGenerators/Source~/AspectGenerator/AspectSyntaxFactory.cs +++ b/Unity.Entities/SourceGenerators/Source~/AspectGenerator/AspectSyntaxFactory.cs @@ -164,7 +164,7 @@ public static string GenerateAspectSource(AspectDefinition aspect) printer.PrintLine(@"/// A container type that provides access to instances of the enclosing Aspect type, indexed by ."); printer.PrintLine(@"/// Equivalent to but for aspect types."); printer.PrintLine(@"/// Constructed from an system state via its constructor."); - printer.PrintBeginLine("public struct Lookup"); + printer.PrintBeginLine($"public struct Lookup : global::Unity.Entities.Internal.InternalCompilerInterface.IAspectLookup<{aspect.Name}>"); { printer.OpenScope(); // Declare all lookup primitives @@ -371,7 +371,7 @@ public static string GenerateAspectSource(AspectDefinition aspect) printer.PrintLine(@$"/// So it completes all write dependencies of the components, buffers, etc. to allow for reading."); printer.PrintLine(@$"/// "); printer.PrintLine(@$"/// The containing an storing all dependencies."); - printer.PrintBeginLine($"public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state)"); + printer.PrintBeginLine($"public void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state)"); printer.OpenScope(); aspect.PrimitivesRouter.PrintCompleteDependency(printer, true); printer.CloseScope(); @@ -382,7 +382,7 @@ public static string GenerateAspectSource(AspectDefinition aspect) printer.PrintLine(@$"/// and it completes all read dependencies, so we can write to it."); printer.PrintLine(@$"/// "); printer.PrintLine(@$"/// The containing an storing all dependencies."); - printer.PrintBeginLine(@$"public static void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state)"); + printer.PrintBeginLine(@$"public void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state)"); printer.OpenScope(); aspect.PrimitivesRouter.PrintCompleteDependency(printer, false); printer.CloseScope(); diff --git a/Unity.Entities/SourceGenerators/Source~/AspectGenerator/SyntaxNodeExt.cs b/Unity.Entities/SourceGenerators/Source~/AspectGenerator/SyntaxNodeExt.cs index 515a704..fdd6ee0 100644 --- a/Unity.Entities/SourceGenerators/Source~/AspectGenerator/SyntaxNodeExt.cs +++ b/Unity.Entities/SourceGenerators/Source~/AspectGenerator/SyntaxNodeExt.cs @@ -1,10 +1,8 @@ -using System; using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.Common; namespace Unity.Entities.SourceGen.Aspect { @@ -16,7 +14,7 @@ public static class SyntaxNodeExt /// /// /// - public static IEnumerable OfKind(this IEnumerable syntaxNode, SyntaxKind kind) => syntaxNode.Where(x => x.IsKind(kind)); + static IEnumerable OfKind(this IEnumerable syntaxNode, SyntaxKind kind) => syntaxNode.Where(x => x.IsKind(kind)); /// /// Reduce to the subset of all the SyntaxToken of a matching SyntaxKind @@ -24,16 +22,8 @@ public static class SyntaxNodeExt /// /// /// - public static IEnumerable OfKind(this IEnumerable syntaxToken, SyntaxKind kind) => syntaxToken.Where(x => x.IsKind(kind)); + static IEnumerable OfKind(this IEnumerable syntaxToken, SyntaxKind kind) => syntaxToken.Where(x => x.IsKind(kind)); - /// - /// Retrieve the first child that is of type T - /// - /// - /// - /// - /// - public static bool TryGetFirstChildByType(this SyntaxNode syntaxNode, out T child) => (child = syntaxNode.ChildNodes().OfType().FirstOrDefault()) != null; /// /// Return if a SyntaxNode has a child token of a given SyntaxKind /// @@ -49,7 +39,7 @@ public static class SyntaxNodeExt /// The SyntaxKind to look for /// The first child of matching syntax kind if found /// true if found. false if not. - public static bool TryGetFirstChildByKind(this SyntaxNode syntaxNode, SyntaxKind kind, out SyntaxNode child) => (child = syntaxNode.ChildNodes().OfKind(kind).FirstOrDefault()) != null; + static bool TryGetFirstChildByKind(this SyntaxNode syntaxNode, SyntaxKind kind, out SyntaxNode child) => (child = syntaxNode.ChildNodes().OfKind(kind).FirstOrDefault()) != null; /// /// Test if a node represent an identifier by comparing string followed @@ -59,7 +49,7 @@ public static class SyntaxNodeExt /// /// /// - public static bool IsIdentifier(this SyntaxNode syntaxNode, string identifier, out TypeArgumentListSyntax typeArgumentListSyntax) + static bool IsIdentifier(this SyntaxNode syntaxNode, string identifier, out TypeArgumentListSyntax typeArgumentListSyntax) { switch (syntaxNode) { @@ -101,7 +91,7 @@ public static bool IsIdentifier(this SyntaxNode syntaxNode, string identifier, o /// The unqualified type name of the generic type. e.g. "Entity" /// output the TypeArgumentListSyntax node if the type represented by this SyntaxNode is generic /// - public static bool IsTypeNameCandidate(this SyntaxNode syntaxNode, string typeNameNamespace, string typeName, out TypeArgumentListSyntax typeArgumentListSyntax) + static bool IsTypeNameCandidate(this SyntaxNode syntaxNode, string typeNameNamespace, string typeName, out TypeArgumentListSyntax typeArgumentListSyntax) { switch (syntaxNode) { @@ -120,15 +110,15 @@ public static bool IsTypeNameCandidate(this SyntaxNode syntaxNode, string typeNa typeArgumentListSyntax = default; return typename == typeNameNamespace; } - else if (qualifiedNameSyntax.Left != null) + + if (qualifiedNameSyntax.Left != null) { // Fast estimate left part without extracting any TypeArgumentListSyntax return qualifiedNameSyntax.Left.IsTypeNameCandidate(typeNameNamespace.Substring(0, iLastDot), typeNameNamespace.Substring(iLastDot + 1)); - } else - { - // Limit the test here, any remaining qualified name is assumed to be a known scope. e.g. part of a using statement or other type defined withing the same unit. - return true; } + + // Limit the test here, any remaining qualified name is assumed to be a known scope. e.g. part of a using statement or other type defined withing the same unit. + return true; default: // Check if current node is the identifier symbolName // and if the current node's scope knows of the scope name symbolNamesapce @@ -136,49 +126,6 @@ public static bool IsTypeNameCandidate(this SyntaxNode syntaxNode, string typeNa } } - /// - /// Figures out as fast as possible if the syntax node does not represent a type name. - /// Use for early-out tests within the OnVisitSyntaxNode calls. - /// Use the SemanticModel from GeneratorExecutionContext.Compilation.GetSemanticModel() to get an accurate result during the Execute call. - /// - /// Returns false if the node is found to *not* be equal using fast early-out tests. - /// Returns true if type name is likely equal and extract the first generic type parameter into genericParam0. - /// - /// Node to test type name against - /// The host namespace of the type name. e.g. "Unity.Entities" - /// The unqualified type name of the generic type. e.g. "ComponentDataRef" - /// TypeSyntax of the first generic type name represented by the SyntaxNode - /// - public static bool IsTypeNameGenericCandidate(this SyntaxNode syntaxNode, string typeNameNamespace, string typeName, out TypeSyntax genericParam0) - { - if (IsTypeNameCandidate(syntaxNode, typeNameNamespace, typeName, out var typeArgumentList)) - { - if (typeArgumentList != null && TryGetFirstChildByType(typeArgumentList, out var typeSyntax)) - { - genericParam0 = typeSyntax; - return true; - } - } - genericParam0 = default; - return false; - } - - /// - /// Perform a string compare between the resolved symbol full type name represented by the SyntaxNode and a given string - /// - /// Node that represent a type name - /// - /// Full type name to compare to - /// - public static bool IsSymbol(this SyntaxNode syntaxNode, SemanticModel model, string symbolTypeName) - { - - var symbol = model.GetSymbolInfo(syntaxNode).Symbol; - if (symbol == null) return false; - var symbolTypeName2 = symbol.GetSymbolType().ToFullName(); - return symbolTypeName2 == symbolTypeName; - } - /// /// Figures out as fast as possible if the node has an attribute that may be equal the to string provided. /// Use for early-out tests within the OnVisitSyntaxNode calls. @@ -204,34 +151,5 @@ public static bool HasAttributeCandidate(this SyntaxNode syntaxNode, string attr } return false; } - - /// - /// Get the full type name of the node's enclosing namespace. - /// - /// - /// e.g. "Unity.Entities" - public static string GetParentNamespace(this SyntaxNode syntaxNode) => String.Join(".", syntaxNode.GetNamespacesFromMostToLeastNested().Reverse().Select(n => n.Name.ToString())); - - - public static bool TryGetGenericParam1TypeName(this SyntaxNode syntaxNode, SemanticModel semanticModel, out string typename) - { - typename = null; - var genericParam1 = syntaxNode.DescendantNodes().OfType().FirstOrDefault()?. - DescendantNodes().OfType().FirstOrDefault(); - if (genericParam1 == null) return false; - typename = semanticModel.GetSymbolInfo(genericParam1).Symbol?.GetSymbolType().ToFullName(); - return typename != null; - } - - public static ISymbol GetGenericParam1Symbol(this SyntaxNode syntaxNode, SemanticModel semanticModel, out string typename) - { - typename = null; - var genericParam1 = syntaxNode.DescendantNodes().OfType().FirstOrDefault()?. - DescendantNodes().OfType().FirstOrDefault(); - if (genericParam1 == null) return null; - var symbol = semanticModel.GetSymbolInfo(genericParam1).Symbol; - typename = symbol?.GetSymbolType().ToFullName(); - return symbol; - } } } diff --git a/Unity.Entities/SourceGenerators/Source~/AspectGenerator/Unity.Entities.SourceGen.AspectGenerator.csproj b/Unity.Entities/SourceGenerators/Source~/AspectGenerator/Unity.Entities.SourceGen.AspectGenerator.csproj index 0e1d132..0f2ed10 100644 --- a/Unity.Entities/SourceGenerators/Source~/AspectGenerator/Unity.Entities.SourceGen.AspectGenerator.csproj +++ b/Unity.Entities/SourceGenerators/Source~/AspectGenerator/Unity.Entities.SourceGen.AspectGenerator.csproj @@ -4,6 +4,7 @@ true netstandard2.0 8.0 + True diff --git a/Unity.Entities/SourceGenerators/Source~/Common/CSharpVerifierHelper.cs b/Unity.Entities/SourceGenerators/Source~/Common/CSharpVerifierHelper.cs index 5e474bc..847ef02 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/CSharpVerifierHelper.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/CSharpVerifierHelper.cs @@ -1,27 +1,25 @@ -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using System; +using System; using System.Collections.Immutable; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; + +namespace Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGenerators.Test +public static class CSharpVerifierHelper { - public static class CSharpVerifierHelper - { - /// - /// By default, the compiler reports diagnostics for nullable reference types at - /// , and the analyzer test framework defaults to only validating - /// diagnostics at . This map contains all compiler diagnostic IDs - /// related to nullability mapped to , which is then used to enable all - /// of these warnings for default validation during analyzer and code fix tests. - /// - public static ImmutableDictionary NullableWarnings { get; } = GetNullableWarningsFromCompiler(); + /// + /// By default, the compiler reports diagnostics for nullable reference types at + /// , and the analyzer test framework defaults to only validating + /// diagnostics at . This map contains all compiler diagnostic IDs + /// related to nullability mapped to , which is then used to enable all + /// of these warnings for default validation during analyzer and code fix tests. + /// + public static ImmutableDictionary NullableWarnings { get; } = GetNullableWarningsFromCompiler(); - private static ImmutableDictionary GetNullableWarningsFromCompiler() - { - string[] args = { "/warnaserror:nullable" }; - var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory); - var nullableWarnings = commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; - return nullableWarnings; - } + static ImmutableDictionary GetNullableWarningsFromCompiler() + { + string[] args = { "/warnaserror:nullable" }; + var commandLineArguments = CSharpCommandLineParser.Default.Parse(args, baseDirectory: Environment.CurrentDirectory, sdkDirectory: Environment.CurrentDirectory); + return commandLineArguments.CompilationOptions.SpecificDiagnosticOptions; } } diff --git a/Unity.Entities/SourceGenerators/Source~/Common/CandidateSyntax.cs b/Unity.Entities/SourceGenerators/Source~/Common/CandidateSyntax.cs new file mode 100644 index 0000000..d064e8e --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/Common/CandidateSyntax.cs @@ -0,0 +1,104 @@ +using System; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Unity.Entities.SourceGen.Common; + +public enum Module +{ + SystemApiContext, + SystemApiQueryBuilder, + Ife, + IJobEntity, + EntityQueryBulkOps, + EntitiesForEach +} + +public readonly struct CandidateSyntax : ISystemCandidate +{ + public CandidateSyntax(CandidateType type, CandidateFlags flags, SyntaxNode node) + { + Type = type; + Flags = flags; + Node = node; + } + + public string CandidateTypeName => Type switch + { + <= CandidateType.MaxSystemAPI => $"SystemAPI.{Type.ToString()}", + CandidateType.Ife => "SystemAPI.Query", + CandidateType.QueryBuilder => "SystemAPI.QueryBuilder", + CandidateType.EntityQueryBulkOps => "EntityQueryBulkOps", + CandidateType.IJobEntity => "IJobEntity", + CandidateType.EntitiesForEach => "Entities.ForEach", + _ => throw new ArgumentOutOfRangeException() + }; + + public SyntaxNode Node { get; } + public readonly CandidateType Type; + public readonly CandidateFlags Flags; + + public static SimpleNameSyntax GetSimpleName(SyntaxNode newestNode) { + if (newestNode is not InvocationExpressionSyntax invocation) + return newestNode as SimpleNameSyntax; + return invocation.Expression switch { + MemberAccessExpressionSyntax member => member.Name, + SimpleNameSyntax sn => sn, + _ => null + }; + } + + public Module GetOwningModule() + { + return Type switch + { + <= CandidateType.MaxSystemAPI => Module.SystemApiContext, + CandidateType.Ife => Module.Ife, + CandidateType.QueryBuilder => Module.SystemApiQueryBuilder, + CandidateType.EntityQueryBulkOps => Module.EntityQueryBulkOps, + CandidateType.IJobEntity => Module.IJobEntity, + CandidateType.EntitiesForEach => Module.EntitiesForEach, + _ => throw new ArgumentOutOfRangeException() + }; + } +} + +public enum CandidateType +{ + TimeData = 1, + GetComponentLookup = 2, + GetComponent = 3, + GetComponentRO = 4, + GetComponentRW = 5, + SetComponent = 6, + HasComponent = 7, + IsComponentEnabled = 8, + SetComponentEnabled = 10, + Singleton = 11, + GetBufferLookup = 12, + GetBuffer = 13, + HasBuffer = 14, + IsBufferEnabled = 15, + SetBufferEnabled = 16, + GetEntityStorageInfoLookup = 17, + Exists = 18, + Aspect = 19, + ComponentTypeHandle = 20, + BufferTypeHandle = 21, + SharedComponentTypeHandle = 22, + EntityTypeHandle = 23, + MaxSystemAPI = 23, + Ife = 24, + IJobEntity = 25, + QueryBuilder = 26, + EntityQueryBulkOps = 27, + EntitiesForEach = 28 +} + +[Flags] +public enum CandidateFlags { + None = 0, + ReadOnly = 1, + NoGenericGeneration = 2, + All = int.MaxValue +} diff --git a/Unity.Entities/SourceGenerators/Source~/Common/ContainerExtensions.cs b/Unity.Entities/SourceGenerators/Source~/Common/ContainerExtensions.cs index 10ef97c..c876989 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/ContainerExtensions.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/ContainerExtensions.cs @@ -1,35 +1,30 @@ using System; using System.Collections.Generic; -namespace Unity.Entities.SourceGen.Common +namespace Unity.Entities.SourceGen.Common; + +public static class ContainerExtensions { - public static class ContainerExtensions + /// + /// Copies the last element of this list to an index. Decrements the length by 1. + /// + /// Useful as a cheap way to remove elements from a list when you don't care about preserving order. + /// The list to remove from + /// The index to overwrite with the last element. + /// Thrown if the index is out of bounds. + public static void RemoveAtSwapBack(this List list, int index) { - /// - /// Copies the last element of this list to an index. Decrements the length by 1. - /// - /// Useful as a cheap way to remove elements from a list when you don't care about preserving order. - /// The list to remove from - /// The index to overwrite with the last element. - /// Thrown if the index is out of bounds. - public static void RemoveAtSwapBack(this List list, int index) - { - // swap to end - (list[index], list[list.Count - 1]) = (list[list.Count - 1], list[index]); - // RemoveAt is O(1) at end - list.RemoveAt(list.Count-1); - } + // swap to end + (list[index], list[list.Count - 1]) = (list[list.Count - 1], list[index]); + // RemoveAt is O(1) at end + list.RemoveAt(list.Count-1); + } - public static void Add(this Dictionary> dictionary, T1 key, T2 value) - { - if (dictionary.TryGetValue(key, out var values)) - { - values.Add(value); - } - else - { - dictionary.Add(key, new List{value}); - } - } + public static void Add(this Dictionary> dictionary, T1 key, T2 value) + { + if (dictionary.TryGetValue(key, out var values)) + values.Add(value); + else + dictionary.Add(key, new List { value }); } } diff --git a/Unity.Entities/SourceGenerators/Source~/Common/Diagnostics.cs b/Unity.Entities/SourceGenerators/Source~/Common/Diagnostics.cs index 0a27dcf..6f72bc9 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/Diagnostics.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/Diagnostics.cs @@ -26,16 +26,17 @@ public interface IDiagnosticLogger : IDisposable /// public class DiagnosticLogger : IDiagnosticLogger { - public GeneratorExecutionContext ExecutionContext; - public List Diagnostics = new List(); - public DiagnosticLogger(GeneratorExecutionContext executionContext) - { - ExecutionContext = executionContext; - } + readonly GeneratorExecutionContext _executionContext; + readonly List _diagnostics = new(); + + public DiagnosticLogger(GeneratorExecutionContext executionContext) => + _executionContext = executionContext; + public void LogError(string errorCode, string title, string errorMessage, Location location, string description = "") { if (errorCode.Contains("ICE")) - errorMessage = CompilerError.WithMessage(errorMessage); + errorMessage = "This error indicates a bug in the DOTS source generators. We'd appreciate a bug report (Help -> Report a Bug...). Thanks! " + + $"Error message: '{errorMessage}'"; Log(DiagnosticSeverity.Error, errorCode, title, errorMessage, location, description); } @@ -71,13 +72,13 @@ void Log(DiagnosticSeverity diagnosticSeverity, string errorCode, string title, } SourceOutputHelpers.LogInfoToSourceGenLog($"{diagnosticSeverity}: {errorCode}, {title}, {errorMessage}"); var rule = new DiagnosticDescriptor(errorCode, title, errorMessage, "Source Generator", diagnosticSeverity, true, description); - Diagnostics.Add(Diagnostic.Create(rule, location)); + _diagnostics.Add(Diagnostic.Create(rule, location)); } public void Dispose() { - foreach (var diagnostic in Diagnostics) - ExecutionContext.ReportDiagnostic(diagnostic); + foreach (var diagnostic in _diagnostics) + _executionContext.ReportDiagnostic(diagnostic); } } diff --git a/Unity.Entities/SourceGenerators/Source~/Common/DiagnosticsLogger.cs b/Unity.Entities/SourceGenerators/Source~/Common/DiagnosticsLogger.cs index 6a108e3..0ec15d1 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/DiagnosticsLogger.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/DiagnosticsLogger.cs @@ -7,21 +7,22 @@ namespace Unity.Entities.SourceGen.SystemGenerator.Common { public static class DiagnosticsLogger { - public static void LogError(this ISourceGeneratorDiagnosable diagnosable, string errorCode, string title, string errorMessage, Location location, string description = "") + public static void LogError(this TDiagnosable diagnosable, string errorCode, string title, string errorMessage, Location location, string description = "") where TDiagnosable : ISourceGeneratorDiagnosable { if (errorCode.Contains("ICE")) - errorMessage = SourceGenHelpers.CompilerError.WithMessage(errorMessage); + errorMessage = "This error indicates a bug in the DOTS source generators. We'd appreciate a bug report (Help -> Report a Bug...). Thanks! " + + $"Error message: '{errorMessage}'"; Log(diagnosable, DiagnosticSeverity.Error, errorCode, title, errorMessage, location, description); } - public static void LogWarning(this ISourceGeneratorDiagnosable diagnosable, string errorCode, string title, string errorMessage, Location location, string description = "") + public static void LogWarning(this TDiagnosable diagnosable, string errorCode, string title, string errorMessage, Location location, string description = "") where TDiagnosable : ISourceGeneratorDiagnosable => Log(diagnosable, DiagnosticSeverity.Warning, errorCode, title, errorMessage, location, description); - public static void LogInfo(this ISourceGeneratorDiagnosable diagnosable, string errorCode, string title, string errorMessage, Location location, string description = "") + public static void LogInfo(this TDiagnosable diagnosable, string errorCode, string title, string errorMessage, Location location, string description = "") where TDiagnosable : ISourceGeneratorDiagnosable => Log(diagnosable, DiagnosticSeverity.Info, errorCode, title, errorMessage, location, description); - static void Log(this ISourceGeneratorDiagnosable diagnosable, DiagnosticSeverity diagnosticSeverity, string errorCode, string title, string errorMessage, Location location, string description = "") + static void Log(this TDiagnosable diagnosable, DiagnosticSeverity diagnosticSeverity, string errorCode, string title, string errorMessage, Location location, string description = "") where TDiagnosable : ISourceGeneratorDiagnosable { if (location.IsInMetadata) throw new InvalidOperationException( @@ -30,12 +31,12 @@ static void Log(this ISourceGeneratorDiagnosable diagnosable, DiagnosticSeverity SourceOutputHelpers.LogInfoToSourceGenLog($"{diagnosticSeverity}: {errorCode}, {title}, {errorMessage}"); var rule = new DiagnosticDescriptor(errorCode, title, errorMessage, "Source Generator", diagnosticSeverity, true, description); - diagnosable.Diagnostics?.Add(Diagnostic.Create(rule, location)); + diagnosable.SourceGenDiagnostics?.Add(Diagnostic.Create(rule, location)); } } public interface ISourceGeneratorDiagnosable { - public List Diagnostics { get; } + public List SourceGenDiagnostics { get; } } } diff --git a/Unity.Entities/SourceGenerators/Source~/Common/EnumerableHelpers.cs b/Unity.Entities/SourceGenerators/Source~/Common/EnumerableHelpers.cs index 5f6fa30..d358edf 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/EnumerableHelpers.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/EnumerableHelpers.cs @@ -7,28 +7,10 @@ namespace Unity.Entities.SourceGen.Common public static class EnumerableHelpers { public static string SeparateBy(this IEnumerable lines, string separator) => string.Join(separator, lines.Where(s => !string.IsNullOrEmpty(s))); - public static string SeparateByDot(this IEnumerable lines) => string.Join(".", lines.Where(s => !string.IsNullOrEmpty(s))); public static string SeparateByComma(this IEnumerable lines) => string.Join(",", lines.Where(s => !string.IsNullOrEmpty(s))); public static string SeparateByCommaAndSpace(this IEnumerable lines) => string.Join(", ", lines.Where(s => !string.IsNullOrEmpty(s))); public static string SeparateByBinaryOr(this IEnumerable lines) => string.Join("|", lines.Where(s => !string.IsNullOrEmpty(s))); - public static string SeparateByCommaAndNewLine(this IEnumerable lines) => string.Join($",{Environment.NewLine}", lines.Where(s => !string.IsNullOrEmpty(s))); public static string SeparateByNewLine(this IEnumerable lines) => string.Join(Environment.NewLine, lines.Where(s => !string.IsNullOrEmpty(s))); - public static string SeparateBySemicolonAndNewLine(this IEnumerable things) => string.Join($";{Environment.NewLine}", things.Where(s => !string.IsNullOrEmpty(s))); - public static string JoinAttributes(this IEnumerable attributes) => string.Join("", attributes.Where(s => !string.IsNullOrEmpty(s)).Select(s => $"[{s}] ")); - - public static IEnumerable DistinctBy( - this IEnumerable source, - Func keySelector) - { - var knownKeys = new HashSet(); - foreach (TSource element in source) - { - if (knownKeys.Add(keySelector(element))) - { - yield return element; - } - } - } public static IEnumerable FindDuplicatesBy( this IEnumerable source, @@ -36,12 +18,8 @@ public static IEnumerable FindDuplicatesBy( { var knownKeys = new HashSet(); foreach (TSource element in source) - { if (!knownKeys.Add(keySelector(element))) - { yield return element; - } - } } } } diff --git a/Unity.Entities/SourceGenerators/Source~/Common/Enums.cs b/Unity.Entities/SourceGenerators/Source~/Common/Enums.cs new file mode 100644 index 0000000..c6d0e40 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/Common/Enums.cs @@ -0,0 +1,36 @@ +using System; +using System.Text; + +namespace Unity.Entities.SourceGen.Common; + +[Flags] +public enum EntityQueryOptions +{ + Default = 0, + IncludePrefab = 1, + IncludeDisabledEntities = 2, + FilterWriteGroup = 4, + IgnoreComponentEnabledState = 8, + IncludeSystems = 16, +} + + +public static class EntityQueryOptionsExtensions +{ + + public static string GetAsFlagStringSeperatedByOr(this EntityQueryOptions options) + { + var orBuilder = new StringBuilder(); + for (var bitIndex = 0; bitIndex < 32; bitIndex++) + { + var currentBit = (EntityQueryOptions)(1< var parseOptionConfigProvider = context.ParseOptionsProvider.Select((options, token) => { var parseOptionsConfig = new ParseOptionConfig(); - var isDotsRuntime = false; - SourceOutputHelpers.Setup(options); foreach (var symbolName in options.PreprocessorSymbolNames) { - isDotsRuntime |= symbolName == "UNITY_DOTSRUNTIME"; parseOptionsConfig.performSafetyChecks |= symbolName == "ENABLE_UNITY_COLLECTIONS_CHECKS"; parseOptionsConfig.isDotsDebugMode |= symbolName == "UNITY_DOTS_DEBUG"; } - parseOptionsConfig.PathIsInFirstAdditionalTextItem = !isDotsRuntime; + parseOptionsConfig.PathIsInFirstAdditionalTextItem = true; return parseOptionsConfig; }); diff --git a/Unity.Entities/SourceGenerators/Source~/Common/Interfaces.cs b/Unity.Entities/SourceGenerators/Source~/Common/Interfaces.cs new file mode 100644 index 0000000..3d54230 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/Common/Interfaces.cs @@ -0,0 +1,22 @@ +using System.CodeDom.Compiler; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; + +namespace Unity.Entities.SourceGen.Common; + +public interface ISystemCandidate +{ + public string CandidateTypeName { get; } + public SyntaxNode Node { get; } +} + +public interface IModuleSyntaxWalker +{ + bool TryWriteSyntax(IndentedTextWriter writer, CandidateSyntax candidateSyntax); +} + +public interface IQueryFieldDescription +{ + public void WriteEntityQueryFieldAssignment(IndentedTextWriter writer, string generatedQueryFieldName); + public string GetFieldDeclaration(string generatedQueryFieldName, bool forcePublic = false); +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Query.cs b/Unity.Entities/SourceGenerators/Source~/Common/Query.cs similarity index 79% rename from Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Query.cs rename to Unity.Entities/SourceGenerators/Source~/Common/Query.cs index 8c928da..f99c6b4 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Query.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/Query.cs @@ -6,7 +6,7 @@ namespace Unity.Entities.SourceGen.SystemGenerator.Common { public enum QueryType { - All, None, Any, ChangeFilter, Disabled, Absent + All, None, Any, ChangeFilter, Disabled, Absent, Present } public struct Query : IEquatable @@ -29,7 +29,7 @@ public override int GetHashCode() public override string ToString() => IsReadOnly - ? $@"Unity.Entities.ComponentType.ReadOnly<{TypeSymbol.ToFullName()}>()" - : $@"Unity.Entities.ComponentType.ReadWrite<{TypeSymbol.ToFullName()}>()"; + ? $"Unity.Entities.ComponentType.ReadOnly<{TypeSymbol.ToFullName()}>()" + : $"Unity.Entities.ComponentType.ReadWrite<{TypeSymbol.ToFullName()}>()"; } } diff --git a/Unity.Entities/SourceGenerators/Source~/Common/SourceGenHelpers.cs b/Unity.Entities/SourceGenerators/Source~/Common/SourceGenHelpers.cs index a527ff7..ec4559f 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/SourceGenHelpers.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/SourceGenHelpers.cs @@ -1,44 +1,24 @@ using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading; -using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; namespace Unity.Entities.SourceGen.Common { public static class SourceGenHelpers { - public const string TrackedNodeAnnotationUsedByRoslyn = "Id"; - - public static SyntaxList GetCompilerGeneratedAttribute() - => AttributeListFromAttributeName("global::System.Runtime.CompilerServices.CompilerGenerated"); - - static SyntaxList AttributeListFromAttributeName(string attributeName) => - new SyntaxList(AttributeList(SingletonSeparatedList(Attribute(IdentifierName(attributeName))))); - - public static class CompilerError - { - public static string WithMessage(string errorMessage) => - "This error indicates a bug in the DOTS source generators. We'd appreciate a bug report (Help -> Report a Bug...). Thanks! " + - $"Error message: '{errorMessage}'"; - } - public static void LogError(this GeneratorExecutionContext context, string errorCode, string title, string errorMessage, Location location, string description = "") { if (errorCode.Contains("ICE")) - errorMessage = CompilerError.WithMessage(errorMessage); - - context.Log(DiagnosticSeverity.Error, errorCode, title, errorMessage, location, description); - } + errorMessage = "This error indicates a bug in the DOTS source generators. We'd appreciate a bug report (Help -> Report a Bug...). Thanks! " + + $"Error message: '{errorMessage}'"; - static void Log(this GeneratorExecutionContext context, DiagnosticSeverity diagnosticSeverity, string errorCode, string title, string errorMessage, Location location, string description = "") - { - SourceOutputHelpers.LogInfoToSourceGenLog($"{diagnosticSeverity}: {errorCode}, {title}, {errorMessage}"); - var rule = new DiagnosticDescriptor(errorCode, title, errorMessage, "Source Generator", diagnosticSeverity, true, description); + SourceOutputHelpers.LogInfoToSourceGenLog($"{DiagnosticSeverity.Error}: {errorCode}, {title}, {errorMessage}"); + var rule = new DiagnosticDescriptor(errorCode, title, errorMessage, "Source Generator", DiagnosticSeverity.Error, true, description); context.ReportDiagnostic(Diagnostic.Create(rule, location)); } @@ -48,8 +28,6 @@ public static bool TryParseQualifiedEnumValue(string value, out TEnum res return Enum.TryParse(unqualifiedEnumValue, out result) && Enum.IsDefined(typeof(TEnum), result); } - public static IEnumerable GetFlags(this Enum e) => Enum.GetValues(e.GetType()).Cast().Where(e.HasFlag); - public static SourceText WithInitialLineDirectiveToGeneratedSource(this SourceText sourceText, string generatedSourceFilePath) { var firstLine = sourceText.Lines.FirstOrDefault(); diff --git a/Unity.Entities/SourceGenerators/Source~/Common/SourceOutputHelpers.cs b/Unity.Entities/SourceGenerators/Source~/Common/SourceOutputHelpers.cs index 29c5759..712feff 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/SourceOutputHelpers.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/SourceOutputHelpers.cs @@ -22,7 +22,6 @@ public static void Setup(ParseOptions parseOptions, ImmutableArray 0 ? $"__{generatorName}" : String.Empty; - if (isSuccess) - fileName = $"{fileName}{postfix}_{stableHashCode}{salting}.g.cs"; - else - fileName = Path.Combine($"{Path.GetRandomFileName()}{postfix}", ".g.cs"); + fileName = isSuccess ? $"{fileName}{postfix}_{stableHashCode}{salting}.g.cs" : Path.Combine($"{Path.GetRandomFileName()}{postfix}", ".g.cs"); return fileName; } - - public static string GetGeneratedSourceFilePath(this SyntaxTree syntaxTree, string assemblyName, string generatorName) + public static (string FullFilePath, string FileNameOnly) GetGeneratedSourceFilePath(this SyntaxTree syntaxTree, string assemblyName, string generatorName, int salting = 0) { - var fileName = GetGeneratedSourceFileName(syntaxTree, generatorName); + var fileName = GetGeneratedSourceFileName(syntaxTree, generatorName, salting); if (CanWriteToProjectPath && s_OutputSourceGenFiles) { var generatedCodePath = GetGeneratedCodePath(); var saveToDirectory = $"{generatedCodePath}/{assemblyName}/"; Directory.CreateDirectory(saveToDirectory); - return $"{saveToDirectory}/{fileName}"; + return ($"{saveToDirectory}/{fileName}".Replace('\\', '/'), fileName.Replace('\\', '/')); } - return $"Temp/GeneratedCode/{assemblyName}/{fileName}"; + return ($"Temp/GeneratedCode/{assemblyName}/{fileName}".Replace('\\', '/'), fileName.Replace('\\', '/')); } static (bool IsSuccess, string FileName) TryGetFileNameWithoutExtension(SyntaxTree syntaxTree) diff --git a/Unity.Entities/SourceGenerators/Source~/Common/StatementHashStack.cs b/Unity.Entities/SourceGenerators/Source~/Common/StatementHashStack.cs deleted file mode 100644 index dc974bb..0000000 --- a/Unity.Entities/SourceGenerators/Source~/Common/StatementHashStack.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Collections.Generic; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; - -namespace Unity.Entities.SourceGen.Common -{ - /// - /// Stack to store statements hashed, without duplicates belonging to a statement. - /// When you pop you get back a list of statements, that all correspond with the same statement. - /// - public struct StatementHashStack - { - public static StatementHashStack CreateInstance() => - new StatementHashStack - { - m_GiveBack = new List(10), - m_LocationInfo = new List<(StatementSyntax, int)>(10), - }; - - List m_GiveBack; - List<(StatementSyntax statement, int endingIndex)> m_LocationInfo; - public StatementSyntax ActiveStatement; - int m_CurrentIndex; - - - /// - /// Pushes a new statement onto the stack. - /// If the last statement matches the current, it'll be part of the same blob. - /// - /// Statement - /// Statement parsable snippet - public void PushStatement(StatementSyntax statement, string statementString) - { - if (ActiveStatement != statement) - { - m_CurrentIndex = m_LocationInfo.Count > 0 ? m_LocationInfo[m_LocationInfo.Count - 1].endingIndex : 0; - m_LocationInfo.Add((statement, m_CurrentIndex)); - ActiveStatement = statement; - } - - var containedInGiveBack = false; - var statementLocationInfo = m_LocationInfo[m_LocationInfo.Count - 1]; - for (var i = m_CurrentIndex; i < statementLocationInfo.endingIndex; i++) - { - containedInGiveBack |= m_GiveBack[i] == statementString; - } - - if (!containedInGiveBack) - { - m_GiveBack.Add(statementString); - statementLocationInfo.endingIndex++; - m_LocationInfo[m_LocationInfo.Count - 1] = statementLocationInfo; - } - } - - /// - /// Returns all statements belonging to the same hash used for the current push - /// ie (a,6), (b,5), (c,5), would return b,c and the stack would now just be (a,6) - /// - /// Statements with equal statement hash - public List PopSyntax() - { - var latestLocation = m_LocationInfo[m_LocationInfo.Count - 1]; - m_LocationInfo.RemoveAt(m_LocationInfo.Count - 1); - - if (m_LocationInfo.Count > 0) - { - var previousLocation = m_LocationInfo[m_LocationInfo.Count - 1]; - ActiveStatement = previousLocation.statement; - m_CurrentIndex = previousLocation.endingIndex; - } - else - { - ActiveStatement = null; - m_CurrentIndex = 0; - } - - var returnList = new List(4); - for (var index = latestLocation.endingIndex - 1; index >= m_CurrentIndex; index--) - { - returnList.Add(SyntaxFactory.ParseStatement(m_GiveBack[index]).WithHiddenLineTrivia() as StatementSyntax); - m_GiveBack.RemoveAt(index); - } - - return returnList; - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/Common/StringHelpers.cs b/Unity.Entities/SourceGenerators/Source~/Common/StringHelpers.cs index d869e29..876b193 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/StringHelpers.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/StringHelpers.cs @@ -1,30 +1,7 @@ -using System; -using System.Linq; - -namespace Unity.Entities.SourceGen.Common +namespace Unity.Entities.SourceGen.Common { public static class StringHelpers { public static string EmitIfTrue(this string emitString, bool someCondition) => someCondition ? emitString : string.Empty; - - public static bool IsValidLambdaName(this string jobName) - { - if (jobName.Length == 0) - return false; - if (char.IsDigit(jobName[0])) - return false; - if (jobName.Any(t => t != '_' && !char.IsLetterOrDigit(t))) - return false; - - // names with __ are reserved for the compiler by convention - return !jobName.Contains("__"); - } - - - // Todo: This is temporary until we have a unified SourceGen printer (as .AppendLine will do the proper thing for us, the problem is from using Verbatim strings) - public static string ReplaceLineEndings(this string value) - { - return value.Replace("\r", "").Replace("\n", Environment.NewLine); - } } } diff --git a/Unity.Entities/SourceGenerators/Source~/Common/SymbolExtensions.cs b/Unity.Entities/SourceGenerators/Source~/Common/SymbolExtensions.cs index 3a07a02..60684d3 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/SymbolExtensions.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/SymbolExtensions.cs @@ -3,13 +3,14 @@ using System.Linq; using System.Text; using Microsoft.CodeAnalysis; +using Unity.Entities.SourceGen.SystemGenerator.Common; namespace Unity.Entities.SourceGen.Common { public static class SymbolExtensions { static SymbolDisplayFormat QualifiedFormat { get; } = - new SymbolDisplayFormat( + new( typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, @@ -18,20 +19,13 @@ public static class SymbolExtensions SymbolDisplayMiscellaneousOptions.UseSpecialTypes); static SymbolDisplayFormat QualifiedFormatWithoutGlobalPrefix { get; } = - new SymbolDisplayFormat( + new( typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers | SymbolDisplayMiscellaneousOptions.UseSpecialTypes); - static SymbolDisplayFormat QualifiedFormatWithoutSpecialTypeNames { get; } = - new SymbolDisplayFormat( - typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces, - genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters, - miscellaneousOptions: - SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers); - public static bool Is(this ITypeSymbol symbol, string fullyQualifiedName, bool checkBaseType = true) { fullyQualifiedName = PrependGlobalIfMissing(fullyQualifiedName); @@ -45,30 +39,20 @@ public static bool Is(this ITypeSymbol symbol, string fullyQualifiedName, bool c return checkBaseType && symbol.BaseType.Is(fullyQualifiedName); } - public static IEnumerable GetAllFullyQualifiedInterfaceAndBaseTypeNames(this ITypeSymbol symbol) - { - if (symbol.BaseType != null) - { - var baseTypeName = symbol.BaseType.ToDisplayString(QualifiedFormat); - if (baseTypeName != "global::System.ValueType") - yield return baseTypeName; - } - - foreach (var _interface in symbol.Interfaces) - yield return _interface.ToDisplayString(QualifiedFormat); - } - public static bool IsInt(this ITypeSymbol symbol) => symbol.SpecialType == SpecialType.System_Int32; + public static bool IsDynamicBuffer(this ITypeSymbol symbol) => symbol.Name == "DynamicBuffer" && symbol.ContainingNamespace.ToDisplayString(QualifiedFormat) == "global::Unity.Entities"; + public static bool IsSharedComponent(this ITypeSymbol symbol) => symbol.InheritsFromInterface("Unity.Entities.ISharedComponentData"); + public static bool IsComponent(this ITypeSymbol symbol) => symbol.InheritsFromInterface("Unity.Entities.IComponentData"); + public static bool IsZeroSizedComponent(this ITypeSymbol symbol, HashSet seenSymbols = null) { // TODO: This was recently fixed (https://github.com/dotnet/roslyn-analyzers/issues/5804), remove pragmas after we update .net #pragma warning disable RS1024 - if (seenSymbols == null) - seenSymbols = new HashSet(SymbolEqualityComparer.Default) { symbol }; + seenSymbols ??= new HashSet(SymbolEqualityComparer.Default) { symbol }; #pragma warning restore RS1024 foreach (var field in symbol.GetMembers().OfType()) @@ -101,7 +85,58 @@ public static bool IsZeroSizedComponent(this ITypeSymbol symbol, HashSet symbol.InheritsFromInterface("Unity.Entities.IEnableableComponent"); public static string ToFullName(this ISymbol symbol) => symbol.ToDisplayString(QualifiedFormat); + + static string ToFullNameIL(this ITypeSymbol symbol) + { + var initialTypeArgument = symbol is INamedTypeSymbol namedTypeSymbol + ? string.Join(",", namedTypeSymbol.TypeArguments.Select(t => t.ToFullNameIL())) + : string.Empty; + + var typeArgumentBuilder = new StringBuilder(initialTypeArgument); + var metaDataName = symbol switch + { + IArrayTypeSymbol array => $"{array.ElementType.ToFullNameIL()}[{(array.Rank == 1 ? string.Empty : string.Join(",", Enumerable.Range(0, array.Rank).Select(_=>"0...")))}]", + IFunctionPointerTypeSymbol fp => $"method {fp.Signature.ReturnType.ToFullNameIL()} *({string.Join(",", fp.Signature.Parameters.Select(p => p.Type.ToFullNameIL()))})", + _ => symbol.MetadataName + }; + var nameBuilder = new StringBuilder(metaDataName); + + // Walk up containing types + for (var containingSymbol = symbol.ContainingSymbol; containingSymbol is not null; containingSymbol = containingSymbol?.ContainingSymbol) + { + switch (containingSymbol) + { + case INamedTypeSymbol containingType when symbol is not ITypeParameterSymbol: + nameBuilder.Insert(0, containingSymbol.MetadataName+'/'); + if (containingType.TypeArguments.Length > 0) + { + var typeArgsFromContainingType = string.Join(",", containingType.TypeArguments.Select(t => t.ToFullNameIL())); + if (typeArgumentBuilder.Length == 0) + typeArgumentBuilder.Append(typeArgsFromContainingType); + else + typeArgumentBuilder.Insert(0, typeArgsFromContainingType+','); + } + break; + case INamespaceSymbol namespaceSymbol when symbol is not ITypeParameterSymbol: + if (!namespaceSymbol.IsGlobalNamespace) + nameBuilder.Insert(0, namespaceSymbol.MetadataName+"."); + break; + } + } + + // Append TypeArguments at the end + if (typeArgumentBuilder.Length > 0) + { + nameBuilder.Append('<'); + nameBuilder.Append(typeArgumentBuilder); + nameBuilder.Append('>'); + } + + return nameBuilder.ToString(); + } + public static string ToSimpleName(this ITypeSymbol symbol) => symbol.ToDisplayString(QualifiedFormatWithoutGlobalPrefix); + public static string ToValidIdentifier(this ITypeSymbol symbol) { var validIdentifier = symbol.ToDisplayString(QualifiedFormatWithoutGlobalPrefix).Replace('.', '_'); @@ -118,17 +153,6 @@ public static bool ImplementsInterface(this ISymbol symbol, string interfaceName && typeSymbol.AllInterfaces.Any(i => i.ToFullName() == interfaceName || i.InheritsFromInterface(interfaceName)); } - public static bool Is(this ITypeSymbol symbol, string nameSpace, string typeName, bool checkBaseType = true) - { - if (symbol is null) - return false; - - if (symbol.Name == typeName && symbol.ContainingNamespace?.Name == nameSpace) - return true; - - return checkBaseType && symbol.BaseType.Is(nameSpace, typeName); - } - public static ITypeSymbol GetSymbolType(this ISymbol symbol) { return symbol switch @@ -167,10 +191,8 @@ public static bool InheritsFromInterface(this ITypeSymbol symbol, string interfa } if (checkBaseType && symbol.BaseType != null) - { if (symbol.BaseType.InheritsFromInterface(interfaceName)) return true; - } return false; } @@ -186,10 +208,8 @@ public static bool InheritsFromType(this ITypeSymbol symbol, string typeName, bo return true; if (checkBaseType && symbol.BaseType != null) - { if (symbol.BaseType.InheritsFromType(typeName)) return true; - } return false; } @@ -197,7 +217,6 @@ public static bool InheritsFromType(this ITypeSymbol symbol, string typeName, bo public static bool HasAttribute(this ISymbol typeSymbol, string fullyQualifiedAttributeName) { fullyQualifiedAttributeName = PrependGlobalIfMissing(fullyQualifiedAttributeName); - return typeSymbol.GetAttributes().Any(attribute => attribute.AttributeClass.ToFullName() == fullyQualifiedAttributeName); } @@ -209,59 +228,40 @@ public static bool HasAttributeOrFieldWithAttribute(this ITypeSymbol typeSymbol, typeSymbol.GetMembers().OfType().Any(f => !f.IsStatic && f.Type.HasAttributeOrFieldWithAttribute(fullyQualifiedAttributeName)); } - public static string GetMethodAndParamsAsString(this IMethodSymbol methodSymbol) + public static string GetMethodAndParamsAsString(this IMethodSymbol methodSymbol, TDiagnostic diagnosticReporter) where TDiagnostic : ISourceGeneratorDiagnosable { var strBuilder = new StringBuilder(); - strBuilder.Append(methodSymbol.Name); - - for (var typeIndex = 0; typeIndex < methodSymbol.TypeParameters.Length; typeIndex++) - strBuilder.Append($"_T{typeIndex}"); + strBuilder.Append(methodSymbol.Name); + strBuilder.Append($"_T{methodSymbol.TypeParameters.Length}"); foreach (var param in methodSymbol.Parameters) { + if (param.RefKind == RefKind.In && !methodSymbol.IsOverride) + strBuilder.Append("_in"); + else if (param.RefKind == RefKind.Out) + strBuilder.Append("_out"); + else if (param.RefKind == RefKind.Ref) + strBuilder.Append("_ref"); + + var paramILName = param.Type.ToFullNameIL(); + if (!string.IsNullOrEmpty(paramILName)) + strBuilder.Append($"_{paramILName}"); + else + { + diagnosticReporter.LogError("SGIL", "ILPP Failure", $"Failed to get IL name for parameter {param.Name}", param.Locations.FirstOrDefault() ?? Location.None); + return ""; + } + if (param.RefKind != RefKind.None) - strBuilder.Append($"_{param.RefKind.ToString().ToLower()}"); - strBuilder.Append($"_{param.Type.ToDisplayString(QualifiedFormatWithoutSpecialTypeNames).Replace(" ", string.Empty)}"); + strBuilder.Append('&'); + if (methodSymbol.IsOverride && param.RefKind == RefKind.In) + strBuilder.Append(" modreq(System.Runtime.InteropServices.InAttribute)"); } - return strBuilder.ToString(); } public static bool IsAspect(this ITypeSymbol typeSymbol) => typeSymbol.InheritsFromInterface("Unity.Entities.IAspect"); - public static TypedConstantKind GetTypedConstantKind(this ITypeSymbol type) - { - switch (type.SpecialType) - { - case SpecialType.System_Boolean: - case SpecialType.System_SByte: - case SpecialType.System_Int16: - case SpecialType.System_Int32: - case SpecialType.System_Int64: - case SpecialType.System_Byte: - case SpecialType.System_UInt16: - case SpecialType.System_UInt32: - case SpecialType.System_UInt64: - case SpecialType.System_Single: - case SpecialType.System_Double: - case SpecialType.System_Char: - case SpecialType.System_String: - case SpecialType.System_Object: - return TypedConstantKind.Primitive; - default: - switch (type.TypeKind) - { - case TypeKind.Array: - return TypedConstantKind.Array; - case TypeKind.Enum: - return TypedConstantKind.Enum; - case TypeKind.Error: - return TypedConstantKind.Error; - } - return TypedConstantKind.Type; - } - } - static string PrependGlobalIfMissing(this string typeOrNamespaceName) => !typeOrNamespaceName.StartsWith("global::") ? $"global::{typeOrNamespaceName}" : typeOrNamespaceName; } diff --git a/Unity.Entities/SourceGenerators/Source~/Common/SyntaxExtensions.cs b/Unity.Entities/SourceGenerators/Source~/Common/SyntaxExtensions.cs index d6bce42..20b3bba 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/SyntaxExtensions.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/SyntaxExtensions.cs @@ -1,5 +1,4 @@ using System; -using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; @@ -10,30 +9,9 @@ namespace Unity.Entities.SourceGen.Common { public static class SyntaxExtensions { - public static IEnumerable GetContainingTypesAndNamespacesFromMostToLeastNested( - this SyntaxNode syntaxNode) - { - SyntaxNode current = syntaxNode; - while (current.Parent != null && (current.Parent is NamespaceDeclarationSyntax || current.Parent is ClassDeclarationSyntax || current.Parent is StructDeclarationSyntax)) - { - yield return current.Parent as MemberDeclarationSyntax; - current = current.Parent; - } - } - public static bool IsReadOnly(this ParameterSyntax parameter) => parameter.Modifiers.Any(mod => mod.IsKind(SyntaxKind.InKeyword)); public static bool IsReadOnly(this IParameterSymbol parameter) => parameter.RefKind == RefKind.In; - public static IEnumerable GetNamespacesFromMostToLeastNested(this SyntaxNode syntaxNode) - { - SyntaxNode current = syntaxNode; - while (current.Parent != null && current.Parent is NamespaceDeclarationSyntax nds) - { - yield return nds; - current = current.Parent; - } - } - class PreprocessorTriviaRemover : CSharpSyntaxRewriter { public override SyntaxTrivia VisitTrivia(SyntaxTrivia trivia) @@ -70,11 +48,13 @@ public static BlockSyntax ToBlockSyntax(this ParenthesizedLambdaExpressionSyntax { if (node.Block != null) return node.Block; - if (node.Body is StatementSyntax lambdaBodyStatement) - return Block(lambdaBodyStatement); - if (node.Body is ExpressionSyntax lambdaBodyExpression) - return Block(SyntaxFactory.ExpressionStatement(lambdaBodyExpression)); - throw new InvalidOperationException($"Invalid lambda body: {node.Body}"); + + return node.Body switch + { + StatementSyntax lambdaBodyStatement => Block(lambdaBodyStatement), + ExpressionSyntax lambdaBodyExpression => Block(ExpressionStatement(lambdaBodyExpression)), + _ => throw new InvalidOperationException($"Invalid lambda body: {node.Body}") + }; } public static bool ContainsDynamicCode(this InvocationExpressionSyntax invoke) @@ -99,43 +79,8 @@ public static T AncestorOfKindOrDefault(this SyntaxNode node) where T : Synta return null; } - public static SyntaxNode WithLineTrivia(this SyntaxNode node, string originalFilePath, int originalLineNumber, int offsetLineNumber = 1) - { - if (string.IsNullOrEmpty(originalFilePath)) - return node; - - var lineTrivia = Comment($"#line {originalLineNumber + offsetLineNumber} \"{originalFilePath}\""); - return node.WithLeadingTrivia(lineTrivia, CarriageReturnLineFeed); - } - public static int GetLineNumber(this SyntaxNode node) => node.GetLocation().GetLineSpan().StartLinePosition.Line; - public static SyntaxNode WithHiddenLineTrivia(this SyntaxNode node) - => node.WithLeadingTrivia(Comment($"#line hidden"), CarriageReturnLineFeed); - - // Walk direct ancestors that are MemberAccessExpressionSyntax and InvocationExpressionSyntax and collect invocations - // This collects things like Entities.WithAll().WithNone().Run() without getting additional ancestor invocations. - public static Dictionary> GetMethodInvocations(this SyntaxNode node) - { - var result = new Dictionary>(); - var parent = node.Parent; - - while (parent is MemberAccessExpressionSyntax memberAccessExpression) - { - parent = parent.Parent; - if (parent is InvocationExpressionSyntax invocationExpression) - { - var memberName = memberAccessExpression.Name.Identifier.ValueText; - result.Add(memberName, invocationExpression); - parent = parent.Parent; - } - else if (!(parent is MemberAccessExpressionSyntax)) - break; - } - - return result; - } - public static string GetModifierString(this ParameterSyntax parameter) { foreach (var mod in parameter.Modifiers) @@ -147,12 +92,5 @@ public static string GetModifierString(this ParameterSyntax parameter) } return ""; } - - public static bool DoesPerformStructuralChange(this InvocationExpressionSyntax syntax, SemanticModel model) - { - return model.GetSymbolInfo(syntax.Expression).Symbol is IMethodSymbol methodSymbol && - methodSymbol.ContainingType.Is("Unity.Entities.EntityManager") && - methodSymbol.HasAttribute("Unity.Entities.EntityManager.StructuralChangeMethodAttribute"); - } } } diff --git a/Unity.Entities/SourceGenerators/Source~/Common/SyntaxTreeInfo.cs b/Unity.Entities/SourceGenerators/Source~/Common/SyntaxTreeInfo.cs index 80ae60c..f3af79b 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/SyntaxTreeInfo.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/SyntaxTreeInfo.cs @@ -11,16 +11,14 @@ public class SyntaxTreeInfo : IEquatable public bool Equals(SyntaxTreeInfo other) { if (ReferenceEquals(null, other)) return false; - if (ReferenceEquals(this, other)) return true; - return Equals(Tree, other.Tree); + return ReferenceEquals(this, other) || Equals(Tree, other.Tree); } public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != this.GetType()) return false; - return Equals((SyntaxTreeInfo)obj); + return obj.GetType() == GetType() && Equals((SyntaxTreeInfo)obj); } public override int GetHashCode() => Tree != null ? Tree.GetHashCode() : 0; diff --git a/Unity.Entities/SourceGenerators/Source~/Common/TypeCreationHelpers.cs b/Unity.Entities/SourceGenerators/Source~/Common/TypeCreationHelpers.cs index 4aef9c3..609b7cf 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/TypeCreationHelpers.cs +++ b/Unity.Entities/SourceGenerators/Source~/Common/TypeCreationHelpers.cs @@ -1,8 +1,6 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.IO; -using System.Linq; using System.Text; using System.Threading; using Microsoft.CodeAnalysis; @@ -19,41 +17,13 @@ public static class TypeCreationHelpers /// public static string GeneratedLineTriviaToGeneratedSource => "// __generatedline__"; - /// - /// Generates the SourceText from a given list of root nodes using the SyntaxTree's path. - /// Includes the existing SyntaxTree's root using statements, - /// and adds correct line directives. - /// - /// Name to base filepath on. - /// Context of the generator executed. - /// Original SyntaxTree to base SourceText location and usings on. - /// Root nodes to add to compilation unit. - /// The SourceText based on nodes and SyntaxTree filepath. - public static SourceText GenerateSourceTextForRootNodes( - string generatorName, - GeneratorExecutionContext generatorExecutionContext, - SyntaxTree originalSyntaxTree, - IEnumerable rootNodes) + public static SourceText FixUpLineDirectivesAndOutputSource( + string generatedSourceFilePath, + string generatedSyntax) { - // Create compilation unit - var existingUsings = - originalSyntaxTree - .GetCompilationUnitRoot(generatorExecutionContext.CancellationToken) - .WithoutPreprocessorTrivia().Usings; - - var compilationUnit = SyntaxFactory.CompilationUnit() - .AddMembers(rootNodes.ToArray()) - .WithoutPreprocessorTrivia() - .WithUsings(existingUsings) - .NormalizeWhitespace(eol:Environment.NewLine); - - var generatedSourceFilePath = originalSyntaxTree.GetGeneratedSourceFilePath( - generatorExecutionContext.Compilation.Assembly.Name, generatorName).Replace('\\', '/'); - // Output as source - var sourceTextForNewClass = - compilationUnit.GetText(Encoding.UTF8) - .WithInitialLineDirectiveToGeneratedSource(generatedSourceFilePath) + var sourceTextForNewClass = SourceText.From(generatedSyntax, Encoding.UTF8) + .WithInitialLineDirectiveToGeneratedSource(generatedSourceFilePath) .WithIgnoreUnassignedVariableWarning(); // Add line directives for lines with `GeneratedLineTriviaToGeneratedSource` or #line @@ -70,137 +40,13 @@ public static SourceText GenerateSourceTextForRootNodes( { var indexOfLineDirective = lineText.IndexOf("#line", StringComparison.Ordinal); textChanges.Add(new TextChange(line.Span, - lineText.Substring(0, indexOfLineDirective - 1) + Environment.NewLine + + lineText.Substring(0, indexOfLineDirective) + Environment.NewLine + lineText.Substring(indexOfLineDirective))); } } - return sourceTextForNewClass.WithChanges(textChanges); } - /// - /// Get root nodes of a replaced SyntaxTree. - /// Where all the original nodes in dictionary is replaced with new nodes. - /// - /// SyntaxTree to look through. - /// Dictionary containing keys of original nodes, and values of replacements. - /// Root nodes of replaced SyntaxTrees. - public static List GetReplacedRootNodes(SyntaxTree syntaxTree, IDictionary originalToReplaced) - { - var newRootNodes = new List(); - var allOriginalNodesAlsoInReplacedTree = originalToReplaced.Keys.SelectMany(node => node.AncestorsAndSelf()).ToImmutableHashSet(); - - foreach (var childNode in syntaxTree.GetRoot().ChildNodes()) - { - switch (childNode) - { - case NamespaceDeclarationSyntax _: - case ClassDeclarationSyntax _: - case StructDeclarationSyntax _: - { - var newRootNode = ConstructReplacedTree(childNode, originalToReplaced, allOriginalNodesAlsoInReplacedTree); - if (newRootNode != null) - newRootNodes.Add(newRootNode); - break; - } - } - } - - return newRootNodes; - } - - /// - /// Constructs a replaced tree based on a root note. - /// Uses originalToReplacedNode to replace. - /// Filtered based on replacementNodeCandidates. - /// - /// Root to replace downwards from. - /// Dictionary containing keys of original nodes, and values of replacements. - /// A list of nodes to look through. (ie. only these nodes will be replaced.) - /// Top member of replaced tree. - /// - /// Happens if currentNode is not a class, namespace or struct. (and is contained in replacementNodeCandidates.) - /// - /// Uses Downwards Recursion. - static MemberDeclarationSyntax ConstructReplacedTree(SyntaxNode currentNode, - IDictionary originalToReplacedNode, - ImmutableHashSet replacementNodeCandidates) - { - // If this node shouldn't exist in replaced tree, early out - if (!replacementNodeCandidates.Contains(currentNode)) - return null; - - // Otherwise, check for replaced children by recursing - var replacedChildren = - currentNode - .ChildNodes() - .Select(childNode => ConstructReplacedTree(childNode, originalToReplacedNode, replacementNodeCandidates)) - .Where(child => child != null).ToArray(); - - // Either get the replaced node for this level - or create one - and add the replaced children - // No node found, need to create a new one to represent this node in the hierarchy - return currentNode switch - { - NamespaceDeclarationSyntax namespaceNode => - SyntaxFactory.NamespaceDeclaration(namespaceNode.Name) - .AddMembers(replacedChildren) - .WithModifiers(namespaceNode.Modifiers) - .WithUsings(namespaceNode.Usings), - - TypeDeclarationSyntax typeNode when originalToReplacedNode.ContainsKey(typeNode) => - originalToReplacedNode[typeNode]?.AddMembers(replacedChildren), - - ClassDeclarationSyntax classNode => - SyntaxFactory.ClassDeclaration(classNode.Identifier) - .AddMembers(replacedChildren) - .WithBaseList(classNode.BaseList) - .WithModifiers(classNode.Modifiers), - - StructDeclarationSyntax structNode => - SyntaxFactory.StructDeclaration(structNode.Identifier) - .AddMembers(replacedChildren) - .WithBaseList(structNode.BaseList) - .WithModifiers(structNode.Modifiers), - - _ => throw new InvalidOperationException($"Expecting class or namespace declaration in syntax tree for {currentNode} but found {currentNode.Kind()}") - }; - } - - static (string start, StringBuilder end) GetBaseDeclaration(BaseTypeDeclarationSyntax typeSyntax) - { - var builderStart = ""; - var builderEnd = new StringBuilder(); - var curliesToClose = 0; - var parentSyntax = typeSyntax.Parent as MemberDeclarationSyntax; - while (parentSyntax != null && ( - parentSyntax.IsKind(SyntaxKind.ClassDeclaration) || - parentSyntax.IsKind(SyntaxKind.StructDeclaration) || - parentSyntax.IsKind(SyntaxKind.RecordDeclaration) || - parentSyntax.IsKind(SyntaxKind.NamespaceDeclaration))) - { - switch (parentSyntax) - { - case TypeDeclarationSyntax parentTypeSyntax: - var keyword = parentTypeSyntax.Keyword.ValueText; // e.g. class/struct/record - var typeName = parentTypeSyntax.Identifier.ToString() + parentTypeSyntax.TypeParameterList; // e.g. Outer/Generic - var constraint = parentTypeSyntax.ConstraintClauses.ToString(); // e.g. where T: new() - builderStart = $"partial {keyword} {typeName} {constraint} {{" + Environment.NewLine + builderStart; - break; - case NamespaceDeclarationSyntax parentNameSpaceSyntax: - builderStart = $"namespace {parentNameSpaceSyntax.Name} {{{Environment.NewLine}{parentNameSpaceSyntax.Usings}" + builderStart; - break; - } - - curliesToClose++; - parentSyntax = parentSyntax.Parent as MemberDeclarationSyntax; - } - - builderEnd.AppendLine(); - builderEnd.Append('}', curliesToClose); - - return (builderStart, builderEnd); - } - public static SourceText GenerateSourceTextForRootNodes( string generatedSourceFilePath, BaseTypeDeclarationSyntax originalSyntax, @@ -258,6 +104,41 @@ public static SourceText GenerateSourceTextForRootNodes( } return sourceTextForNewClass.WithChanges(textChanges); + + static (string start, StringBuilder end) GetBaseDeclaration(BaseTypeDeclarationSyntax typeSyntax) + { + var builderStart = ""; + var builderEnd = new StringBuilder(); + var curliesToClose = 0; + var parentSyntax = typeSyntax.Parent as MemberDeclarationSyntax; + while (parentSyntax != null && ( + parentSyntax.IsKind(SyntaxKind.ClassDeclaration) || + parentSyntax.IsKind(SyntaxKind.StructDeclaration) || + parentSyntax.IsKind(SyntaxKind.RecordDeclaration) || + parentSyntax.IsKind(SyntaxKind.NamespaceDeclaration))) + { + switch (parentSyntax) + { + case TypeDeclarationSyntax parentTypeSyntax: + var keyword = parentTypeSyntax.Keyword.ValueText; // e.g. class/struct/record + var typeName = parentTypeSyntax.Identifier.ToString() + parentTypeSyntax.TypeParameterList; // e.g. Outer/Generic + var constraint = parentTypeSyntax.ConstraintClauses.ToString(); // e.g. where T: new() + builderStart = $"partial {keyword} {typeName} {constraint} {{" + Environment.NewLine + builderStart; + break; + case NamespaceDeclarationSyntax parentNameSpaceSyntax: + builderStart = $"namespace {parentNameSpaceSyntax.Name} {{{Environment.NewLine}{parentNameSpaceSyntax.Usings}" + builderStart; + break; + } + + curliesToClose++; + parentSyntax = parentSyntax.Parent as MemberDeclarationSyntax; + } + + builderEnd.AppendLine(); + builderEnd.Append('}', curliesToClose); + + return (builderStart, builderEnd); + } } } } diff --git a/Unity.Entities/SourceGenerators/Source~/Common/Unity.Entities.SourceGen.Common.csproj b/Unity.Entities/SourceGenerators/Source~/Common/Unity.Entities.SourceGen.Common.csproj index 11cdd3b..44355a4 100644 --- a/Unity.Entities/SourceGenerators/Source~/Common/Unity.Entities.SourceGen.Common.csproj +++ b/Unity.Entities/SourceGenerators/Source~/Common/Unity.Entities.SourceGen.Common.csproj @@ -3,7 +3,9 @@ true netstandard2.0 - 8.0 + latest + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.CodeFixes/EntitiesCodeFixProvider.cs b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.CodeFixes/EntitiesCodeFixProvider.cs index c6afbc7..71e0c3d 100644 --- a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.CodeFixes/EntitiesCodeFixProvider.cs +++ b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.CodeFixes/EntitiesCodeFixProvider.cs @@ -1,4 +1,5 @@ -using Microsoft.CodeAnalysis; +using System; +using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CodeActions; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.Syntax; @@ -9,6 +10,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp; +using Unity.Entities.SourceGen.Common; namespace Unity.Entities.Analyzer { @@ -16,6 +18,7 @@ namespace Unity.Entities.Analyzer public class EntitiesCodeFixProvider : CodeFixProvider { public sealed override ImmutableArray FixableDiagnosticIds => ImmutableArray.Create( + CSharpCompilerDiagnostics.CS1654, EntitiesDiagnostics.ID_EA0001, EntitiesDiagnostics.ID_EA0007, EntitiesDiagnostics.ID_EA0008, @@ -28,23 +31,25 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) foreach (var diagnostic in context.Diagnostics) { var node = root?.FindNode(diagnostic.Location.SourceSpan); - if (node is LocalDeclarationStatementSyntax {Declaration:{} variableDeclaration} && diagnostic.Id == EntitiesDiagnostics.ID_EA0001) { - context.RegisterCodeFix( - CodeAction.Create(title: "Use non-readonly reference", - createChangedDocument: c => MakeNonReadonlyReference(context.Document, variableDeclaration, c), - equivalenceKey: "NonReadonlyReference"), - diagnostic); - } - - if (node is ParameterSyntax parameterSyntax && diagnostic.Id == EntitiesDiagnostics.ID_EA0009) { - context.RegisterCodeFix( - CodeAction.Create(title: "Use non-readonly reference", - createChangedDocument: c => MakeNonReadonlyReference(context.Document, parameterSyntax, c), - equivalenceKey: "NonReadonlyReferenceParameter"), - diagnostic); - } - if (node is TypeDeclarationSyntax typeDeclarationSyntax) { + switch (node) + { + case LocalDeclarationStatementSyntax {Declaration:{} variableDeclaration} when diagnostic.Id == EntitiesDiagnostics.ID_EA0001: + context.RegisterCodeFix( + CodeAction.Create(title: "Use non-readonly reference", + createChangedDocument: c => MakeNonReadonlyReference(context.Document, variableDeclaration, c), + equivalenceKey: "NonReadonlyReference"), + diagnostic); + break; + case ParameterSyntax parameterSyntax when diagnostic.Id == EntitiesDiagnostics.ID_EA0009: + context.RegisterCodeFix( + CodeAction.Create(title: "Use non-readonly reference", + createChangedDocument: c => MakeNonReadonlyReference(context.Document, parameterSyntax, c), + equivalenceKey: "NonReadonlyReferenceParameter"), + diagnostic); + break; + case TypeDeclarationSyntax typeDeclarationSyntax: + { if (diagnostic.Id == EntitiesDiagnostics.ID_EA0007 || diagnostic.Id == EntitiesDiagnostics.ID_EA0008) { context.RegisterCodeFix( @@ -61,8 +66,71 @@ public sealed override async Task RegisterCodeFixesAsync(CodeFixContext context) equivalenceKey: "AddBurstCompilerAttribute"), diagnostic); } + + break; + } + case ElementAccessExpressionSyntax elementAccessExpression when diagnostic.Id == CSharpCompilerDiagnostics.CS1654: + { + context.RegisterCodeFix( + CodeAction.Create(title: "Store element in a new variable", + createChangedDocument: c => AssignCurrentVariableToNewVariable_AndUseNewVariableInstead(context.Document, elementAccessExpression, c), + equivalenceKey: "StoreValueTypeElementInNewVariable"), + diagnostic); + break; + } + } + } } - } + + // To understand the implementation of this method, it helps to read through some sample code here: + // https://sharplab.io/#v2:C4LghgzsA+ACBMBGAsAKDbAzAAigJwFcBjYbAEQE8A7MAWwEsiAhAgM1YFM8AeAFQD5saAN5ps47Fmy9swABb0IAbXpVSqgCYcAHgF0xE0agknsAcw6kAvIK2swBADbAAFLwCUAbgOncl7DbYAG5gjgQcAdh2Ds5uXj7YAL5oyeioUgjYAMLYRiYZACzYALIu7kLGhgkmrAD2eBxgRHLYLi4heFEARogANMFgnRpd8OWq2FQcAO6tlDQMzGycPKrA/P1zdIws7FzcrI61YGvuSgAMurmJ7tVVlb4mAPQAVNgA6hFTYGqytdgNUzw9GAEXkEUm2lIjlUoNqIFuvgRDxMHQm02GiEiGO892RD0mUwx50uVmwiBxeLxSIe43qWk6wD+DQgtUcQQiWQAyogAGwAVgKADpqYjcZTpH8OFQIAQGtgiPUGiRsJopcBjvRalRsLVWLI5ODpo4KCrpVwQRpsNDJv1gWiOBoIL9sAQIKCDbIgUF6GBsJAWUQfRbsFNgS0wRVxSZ6Fo1PRWPQuNgAAbDZOm/WGyFWmHCsUPZ6PEXiIkXSLk4uR8Uvd6fb6kRn/aZAkGZtHZ62w+H50VR3yogkYrE9Cl9lGDNGEkbD+CjsfiVHMpzWVqDnrE8vlADUq/RIw3pMQgrOrHiPfFlYktLw9OdzNZ7Oy3P5Qsv4jfEuwUplcoVeCV6hULG6rAJq2q6m2BLGqabp4MGnYQP0YIml0tTyPalqNq67qgl6Pp+hAAZBg6IZhh+EbJh06Yxmq8aJp04wRhCUK5pWhaVouHAys4kQuKWJJkturTDPAB5ksep5zqYqQmKkiRAA== + static async Task AssignCurrentVariableToNewVariable_AndUseNewVariableInstead( + Document document, ElementAccessExpressionSyntax elementAccessExpression, CancellationToken cancellationToken) + { + var currentIdentifierName = (IdentifierNameSyntax)elementAccessExpression.Expression; + var currentIdentifier = currentIdentifierName.Identifier; + var currentIdentifierText = currentIdentifier.ValueText; + var currentBracketedArgumentList = elementAccessExpression.ArgumentList; + + // Declare a new variable and assign the current variable to it. + var newIdentifier = SyntaxFactory.IdentifierName($"__new{currentIdentifierText}__").WithTriviaFrom(currentIdentifierName); + var variableDeclaratorSyntax = + SyntaxFactory.VariableDeclarator( + newIdentifier.Identifier.WithTrailingTrivia(SyntaxFactory.Space), + argumentList: null, + initializer: SyntaxFactory.EqualsValueClause(currentIdentifierName.WithLeadingTrivia(SyntaxFactory.Space))); + + // Insert the new variable declaration before the statement containing the current variable. + var insertNewVariableDeclarationBefore = elementAccessExpression.AncestorOfKind(); + + // If the original code is: `db[0] = blah`, then the new variable declaration needs to use the trivia associated with `db` in order to preserve + // the correct indentation. + // If the original code is: `var result = (db1[0] = blah) + (db2[0] = blah);` then the new variable declaration needs to use the trivia associated with + // `var` in order to preserve the correct indentation. + // To retrieve the correct trivia, we simply need to look for the first `IdentifierNameSyntax` node in the statement containing the current variable, + // and use its trivia. + var variableDeclarationUseTriviaFrom = insertNewVariableDeclarationBefore.DescendantNodes().OfType().First(); + var variableDeclarationStatement = + SyntaxFactory.ParseStatement($"var {variableDeclaratorSyntax.ToString()};") + .WithLeadingTrivia(variableDeclarationUseTriviaFrom.Identifier.LeadingTrivia) + .WithTrailingTrivia(SyntaxFactory.EndOfLine("\n")); + + var newElementAccessNode = SyntaxFactory.ElementAccessExpression(newIdentifier, currentBracketedArgumentList).WithTriviaFrom(currentIdentifierName); + + var currentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false); + + // Because we are making multiple changes, and each change creates a new COPY of the document with the change applied, we need to track + // the original nodes we want to modify across all new copies to ensure that our changes are always correctly applied. Each time we apply a change, we + // must invoke `trackedRoot.GetCurrentNode(originalNodeToModify)` to identify the node in the new copy that corresponds to `originalNodeToModify`. + var trackedRoot = currentRoot?.TrackNodes(insertNewVariableDeclarationBefore, elementAccessExpression); + var trackedStatement = trackedRoot?.GetCurrentNode(insertNewVariableDeclarationBefore); + trackedRoot = trackedRoot.InsertNodesBefore(trackedStatement, new SyntaxNode[]{variableDeclarationStatement}); + var trackedElementAccessNode = trackedRoot.GetCurrentNode(elementAccessExpression); + trackedRoot = trackedRoot.ReplaceNode(trackedElementAccessNode, newElementAccessNode.WithTrailingTrivia(SyntaxFactory.Space)); + + Debug.Assert(currentRoot != null, nameof(currentRoot) + " != null"); + + return document.WithSyntaxRoot(trackedRoot); } static async Task AddPartial(Document document, TypeDeclarationSyntax typeDeclarationSyntax, CancellationToken cancellationToken) diff --git a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.CodeFixes/Unity.Entities.Analyzer.CodeFixes.csproj b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.CodeFixes/Unity.Entities.Analyzer.CodeFixes.csproj index a3e0de4..35e8568 100644 --- a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.CodeFixes/Unity.Entities.Analyzer.CodeFixes.csproj +++ b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.CodeFixes/Unity.Entities.Analyzer.CodeFixes.csproj @@ -4,8 +4,8 @@ true netstandard2.0 8.0 - Unity.Entities.Analyzer.CodeFixes - Unity.Entities.Analyzer.CodeFixes + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/CSharpCompilerErrorCodeFixTests.cs b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/CSharpCompilerErrorCodeFixTests.cs new file mode 100644 index 0000000..7a3567c --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/CSharpCompilerErrorCodeFixTests.cs @@ -0,0 +1,94 @@ +using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Unity.Entities.Analyzer.Test; + +namespace Unity.Entities.Analyzer; + +[TestClass] +public class CSharpCompilerErrorCodeFixTests +{ + [TestMethod] + public async Task CS1654_ModifyingValueTypeElementsNotClassifiedAsVariables() + { + const string faultyCode = @" + using Unity.Entities; + using Unity.Entities.Tests; + using static Unity.Entities.SystemAPI; + + partial class TestSystem : SystemBase + { + protected override void OnUpdate() + { + foreach (var (ecsTestData, db) in SystemAPI.Query, DynamicBuffer>()) + { + {|#0:db[0]|} = new EcsIntElement { Value = 1 }; + } + } + }"; + + const string fixedCode = @" + using Unity.Entities; + using Unity.Entities.Tests; + using static Unity.Entities.SystemAPI; + + partial class TestSystem : SystemBase + { + protected override void OnUpdate() + { + foreach (var (ecsTestData, db) in SystemAPI.Query, DynamicBuffer>()) + { + var __newdb__ = db; + __newdb__[0] = new EcsIntElement { Value = 1 }; + } + } + }"; + + await CSharpCodeFixVerifier.VerifyCodeFixAsync( + faultyCode, + DiagnosticResult.CompilerError("CS1654").WithLocation(0).WithArguments("db", "foreach iteration variable"), + fixedCode); + } + + [TestMethod] + public async Task CS1654_ModifyingValueTypeElementsNotClassifiedAsVariables_EdgeCase() + { + const string faultyCode = @" + using Unity.Entities; + using Unity.Entities.Tests; + using static Unity.Entities.SystemAPI; + + partial class TestSystem : SystemBase + { + protected override void OnUpdate() + { + foreach (var (a, b) in SystemAPI.Query, DynamicBuffer>()) + { + var test = (({|#0:a[0]|} = 5) + ({|#1:b[0]|} = 5.0f)); + } + } + }"; + + const string fixedCode = @" + using Unity.Entities; + using Unity.Entities.Tests; + using static Unity.Entities.SystemAPI; + + partial class TestSystem : SystemBase + { + protected override void OnUpdate() + { + foreach (var (a, b) in SystemAPI.Query, DynamicBuffer>()) + { + var __newa__ = a; + var __newb__ = b; + var test = ((__newa__[0] = 5) + (__newb__[0] = 5.0f)); + } + } + }"; + + var expected1 = DiagnosticResult.CompilerError("CS1654").WithLocation(0).WithArguments("a", "foreach iteration variable"); + var expected2 = DiagnosticResult.CompilerError("CS1654").WithLocation(1).WithArguments("b", "foreach iteration variable"); + await CSharpCodeFixVerifier.VerifyCodeFixAsync(faultyCode, new []{expected1, expected2}, fixedCode, numIterationsExpected: 2); + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/Unity.Entities.Analyzer.Test.csproj b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/Unity.Entities.Analyzer.Test.csproj index 79ddb13..ed3db0e 100644 --- a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/Unity.Entities.Analyzer.Test.csproj +++ b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/Unity.Entities.Analyzer.Test.csproj @@ -6,6 +6,8 @@ enable false Unity.Entities.Analyzer.Test + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/Verifiers/CSharpCodeFixVerifier.cs b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/Verifiers/CSharpCodeFixVerifier.cs index 178d2ee..1b67d70 100644 --- a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/Verifiers/CSharpCodeFixVerifier.cs +++ b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer.Test/Verifiers/CSharpCodeFixVerifier.cs @@ -11,7 +11,7 @@ using System.Threading.Tasks; using Unity.Burst; using Unity.Collections; -using Unity.Entities.SourceGenerators.Test; +using Unity.Entities.SourceGen.Common; namespace Unity.Entities.Analyzer.Test { @@ -53,13 +53,14 @@ public static async Task VerifyCodeFixAsync(string source, DiagnosticResult expe => await VerifyCodeFixAsync(source, new[] { expected }, fixedSource); /// - public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource) + public static async Task VerifyCodeFixAsync(string source, DiagnosticResult[] expected, string fixedSource, int numIterationsExpected = 1) { var test = new Test { TestState = { AdditionalReferences = {typeof(EntitiesMock).Assembly, typeof(BurstMock).Assembly, typeof(CollectionsMock).Assembly}}, TestCode = source, FixedCode = fixedSource, + NumberOfFixAllIterations = numIterationsExpected }; test.ExpectedDiagnostics.AddRange(expected); diff --git a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer/Diagnostics.cs b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer/Diagnostics.cs index aee5ae1..b1bd2db 100644 --- a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer/Diagnostics.cs +++ b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer/Diagnostics.cs @@ -2,6 +2,11 @@ namespace Unity.Entities.Analyzer { + public static class CSharpCompilerDiagnostics + { + public const string CS1654 = "CS1654"; + } + public static class EntitiesDiagnostics { public const string ID_EA0001 = "EA0001"; diff --git a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer/Unity.Entities.Analyzer.csproj b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer/Unity.Entities.Analyzer.csproj index a746bfe..911ae8d 100644 --- a/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer/Unity.Entities.Analyzer.csproj +++ b/Unity.Entities/SourceGenerators/Source~/EntitiesAnalyzer/Unity.Entities.Analyzer/Unity.Entities.Analyzer.csproj @@ -5,8 +5,8 @@ RS2007 netstandard2.0 9 - Unity.Entities.Analyzer - Unity.Entities.Analyzer + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/ComponentRefWrapperType.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/ComponentRefWrapperType.cs new file mode 100644 index 0000000..70df7bd --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/ComponentRefWrapperType.cs @@ -0,0 +1,11 @@ +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +public enum ComponentRefWrapperType +{ + NotApplicable, + None, + RefRO, + RefRW, + EnabledRefRO, + EnabledRefRW +} diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/IjeSchedulingSyntaxWalker.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/IjeSchedulingSyntaxWalker.cs new file mode 100644 index 0000000..7080efe --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/IjeSchedulingSyntaxWalker.cs @@ -0,0 +1,266 @@ +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; +using static Unity.Entities.SourceGen.JobEntityGenerator.JobEntityModule; + +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +/* + The `IjeSchedulingWalker` traverses through syntax nodes that have been marked by the `JobEntityModule` as candidates for patching -- specifically, + scheduling invocations of `IJobEntity` instances. The `IjeSchedulingWalker`, like the `SystemApiWalker`, needs to handle nested candidates, e.g. + + new UsingCodeGenInsideAScheduleObjectInitJob + { + datas = SystemAPI.GetComponentLookup() + }.Schedule(SystemAPI.QueryBuilder().WithAll().Build(), Dependency).Complete(); + + In order to patch the example above correctly, the `IjeSchedulingSyntaxWalker` needs to cede write control to the `SystemApiContextWalker` when it reaches + the `SystemAPI.GetComponentLookup()` node, and to the `SystemApiQueryBuilderSyntaxWalker` when it reaches the `SystemAPI.QueryBuilder().WithAll().Build()` + node. It then gathers the patches made by the two other walkers, and ends up writing the following code to replace the original code above: + + __ScheduleViaJobChunkExtension_15(new UsingCodeGenInsideAScheduleObjectInitJob + { + datas = global::Unity.Entities.Internal.InternalCompilerInterface.GetComponentLookup(ref __TypeHandle.__Unity_Entities_Tests_EcsTestData_RW_ComponentLookup, ref this.CheckedStateRef) + }, + __query_1239178100_0, Dependency, ref this.CheckedStateRef, true).Complete() + + The resultant `global::Unity.Entities.Internal.InternalCompilerInterface.GetComponentLookup(ref __TypeHandle.__Unity_Entities_Tests_EcsTestData_RW_ComponentLookup, ref this.CheckedStateRef)` + was generated by the `SystemApiContextWalker`, and `__query_1239178100_0` by the `SystemApiQueryBuilderSyntaxWalker`. + */ +public class IjeSchedulingSyntaxWalker : CSharpSyntaxWalker, IModuleSyntaxWalker +{ + private readonly Dictionary _schedulingInvocationNodes; + private readonly StringWriter _schedulingArgsInnerWriter; + private readonly IndentedTextWriter _schedulingArgsWriter; + + private IndentedTextWriter _writer; + private bool _hasWrittenSyntax; + private int _uniqueId; + private bool _isWalkingSchedulingInvocationArgument; + private SystemDescription _systemDescription; + + private string _schedulingJobEntityInstanceArgument; + private string _userDefinedQueryArgument; + private string _userDefinedDependency; + private ObjectCreationExpressionSyntax _jobEntityInstanceCreationSyntax; + + internal IjeSchedulingSyntaxWalker(ref SystemDescription systemDescription, IReadOnlyCollection jobEntityInfos) + : base(SyntaxWalkerDepth.Trivia) + { + _uniqueId = 0; + _systemDescription = systemDescription; + _schedulingInvocationNodes = + jobEntityInfos.GroupBy(info => info.Candidate.Invocation) + .ToDictionary(group => group.Key, group => group.Single()); + + _schedulingArgsInnerWriter = new StringWriter(); + _schedulingArgsWriter = new IndentedTextWriter(_schedulingArgsInnerWriter); + } + + public bool TryWriteSyntax(IndentedTextWriter writer, CandidateSyntax candidateSyntax) + { + _writer = writer; + _hasWrittenSyntax = false; + + _schedulingArgsInnerWriter.GetStringBuilder().Clear(); + _isWalkingSchedulingInvocationArgument = false; + + // Begin depth-first traversal of the candidate node + Visit(candidateSyntax.Node); + + return _hasWrittenSyntax; + } + + public override void VisitInvocationExpression(InvocationExpressionSyntax node) + { + if (_isWalkingSchedulingInvocationArgument) + { + if (_systemDescription.CandidateNodes.TryGetValue(node, out var candidateSyntax)) + { + var rewroteArg = _systemDescription.SyntaxWalkers[candidateSyntax.GetOwningModule()].TryWriteSyntax(_schedulingArgsWriter, candidateSyntax); + if (!rewroteArg) + base.VisitInvocationExpression(node); + } + else + base.VisitInvocationExpression(node); + } + else if (_schedulingInvocationNodes.TryGetValue(node, out var jobEntityInfo)) + { + var tryGetJobArg = jobEntityInfo.TryGetJobArgumentUsedInSchedulingInvocation(); + if (tryGetJobArg.Success) + { + if (tryGetJobArg.ObjectCreationExpressionSyntax != null) + { + _isWalkingSchedulingInvocationArgument = true; + + /* The user might have used sourcegen-dependent API when creating a new `IJobEntity` instance, e.g. + + new UsingCodeGenInsideAScheduleObjectInitJob + { + datas = SystemAPI.GetComponentLookup() + }.Schedule(SystemAPI.QueryBuilder().WithAll().Build(), Dependency).Complete(); + + Thus we need to walk the object creation syntax, and cede write control to the appropriate syntax walker if necessary. + In the example above, we need to cede write control to the `SystemApiContextWalker` when we reach the + `SystemAPI.GetComponentLookup()` node, so that the `SystemApiContextWalker` can patch it accordingly. + */ + _jobEntityInstanceCreationSyntax = tryGetJobArg.ObjectCreationExpressionSyntax; + VisitObjectCreationExpression(_jobEntityInstanceCreationSyntax); + + _isWalkingSchedulingInvocationArgument = false; + } + else + { + _jobEntityInstanceCreationSyntax = null; + _schedulingJobEntityInstanceArgument = tryGetJobArg.IdentifierNameSyntax.ToString(); + } + } + + var result = + JobEntityInstanceInfo.GetUserDefinedQueryAndDependency(ref _systemDescription, jobEntityInfo.IsExtensionMethodUsed, node); + + if (result.UserDefinedEntityQuery != null) + { + _isWalkingSchedulingInvocationArgument = true; + + // The user might have used sourcegen-dependent API to create a query, so we need to walk it, + // and cede write control to the appropriate syntax walker if necessary. In the example shown in the + // previous comment, we will need to cede write control to the `SystemApiQueryBuilderWalker` when + // we reach the `SystemAPI.QueryBuilder().WithAll().Build()` node, so that the + // `SystemApiQueryBuilderWalker` can patch it accordingly. + base.Visit(result.UserDefinedEntityQuery); + + _userDefinedQueryArgument = _schedulingArgsInnerWriter.ToString(); + + // StringWriter.Flush() unfortunately doesn't clear the buffer correctly: https://stackoverflow.com/a/13706647 + // Since IndentedTextWriter.Flush() calls stringWriter.Flush(), we cannot use it either. + _schedulingArgsInnerWriter.GetStringBuilder().Clear(); + _isWalkingSchedulingInvocationArgument = false; + } + else + _userDefinedQueryArgument = null; + + _userDefinedDependency = result.UserDefinedDependency?.ToString(); + + string replacementCode = + jobEntityInfo.GetAndAddScheduleExpression( + ref _systemDescription, + _uniqueId++, + tryGetJobArg.JobArgumentIndexInExtensionMethod, + _schedulingJobEntityInstanceArgument, + _userDefinedQueryArgument, + _userDefinedDependency); + + _writer.Write(replacementCode); + _hasWrittenSyntax = true; + } + else + _hasWrittenSyntax = false; + } + + public override void VisitObjectCreationExpression(ObjectCreationExpressionSyntax node) + { + base.VisitObjectCreationExpression(node); + + if (node == _jobEntityInstanceCreationSyntax) + { + _schedulingJobEntityInstanceArgument = _schedulingArgsInnerWriter.ToString(); + + // StringWriter.Flush() unfortunately doesn't clear the buffer correctly: https://stackoverflow.com/a/13706647 + // Since IndentedTextWriter.Flush() calls stringWriter.Flush(), we cannot use it either. + _schedulingArgsInnerWriter.GetStringBuilder().Clear(); + } + } + + public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node) + { + if (_isWalkingSchedulingInvocationArgument) + { + if (_systemDescription.CandidateNodes.TryGetValue(node, out var candidateSyntax)) + { + var rewroteArg = _systemDescription.SyntaxWalkers[candidateSyntax.GetOwningModule()].TryWriteSyntax(_schedulingArgsWriter, candidateSyntax); + if (!rewroteArg) + base.VisitMemberAccessExpression(node); + } + else + base.VisitMemberAccessExpression(node); + } + else + base.VisitMemberAccessExpression(node); + } + + public override void VisitIdentifierName(IdentifierNameSyntax node) + { + if (_isWalkingSchedulingInvocationArgument) + { + if (_systemDescription.CandidateNodes.TryGetValue(node, out var candidateSyntax)) + { + var rewroteArg = _systemDescription.SyntaxWalkers[candidateSyntax.GetOwningModule()].TryWriteSyntax(_schedulingArgsWriter, candidateSyntax); + if (!rewroteArg) + base.VisitIdentifierName(node); + } + else + base.VisitIdentifierName(node); + } + else + base.VisitIdentifierName(node); + } + public override void VisitToken(SyntaxToken token) + { + VisitLeadingTrivia(token); + + if (_isWalkingSchedulingInvocationArgument) + _schedulingArgsWriter.Write(token.Text); + else + _writer.Write(token.Text); + + VisitTrailingTrivia(token); + } + + public override void VisitTrivia(SyntaxTrivia trivia) + { + var triviaKind = trivia.Kind(); + + if (triviaKind == SyntaxKind.EndOfLineTrivia) + { + if (_isWalkingSchedulingInvocationArgument) + _schedulingArgsWriter.WriteLine(); + else + _writer.WriteLine(); + } + + else if (triviaKind != SyntaxKind.DisabledTextTrivia && + triviaKind != SyntaxKind.PreprocessingMessageTrivia && + triviaKind != SyntaxKind.IfDirectiveTrivia && + triviaKind != SyntaxKind.ElifDirectiveTrivia && + triviaKind != SyntaxKind.ElseDirectiveTrivia && + triviaKind != SyntaxKind.EndIfDirectiveTrivia && + triviaKind != SyntaxKind.RegionDirectiveTrivia && + triviaKind != SyntaxKind.EndRegionDirectiveTrivia && + triviaKind != SyntaxKind.DefineDirectiveTrivia && + triviaKind != SyntaxKind.UndefDirectiveTrivia && + triviaKind != SyntaxKind.ErrorDirectiveTrivia && + triviaKind != SyntaxKind.WarningDirectiveTrivia && + triviaKind != SyntaxKind.PragmaWarningDirectiveTrivia && + triviaKind != SyntaxKind.PragmaChecksumDirectiveTrivia && + triviaKind != SyntaxKind.ReferenceDirectiveTrivia && + triviaKind != SyntaxKind.BadDirectiveTrivia && + triviaKind != SyntaxKind.SingleLineCommentTrivia && + triviaKind != SyntaxKind.MultiLineCommentTrivia) + { + if (!trivia.HasStructure) + { + if (_isWalkingSchedulingInvocationArgument) + _schedulingArgsWriter.Write(trivia.ToString()); + else + _writer.Write(trivia.ToString()); + } + } + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescription.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescription.cs index 6e80b8c..8f42425 100644 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescription.cs @@ -6,244 +6,140 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +public struct ParameterTypeInJobEntityExecuteMethod { - public struct ParameterTypeInJobEntityExecuteMethod - { - public bool IsReadOnly; - public ITypeSymbol TypeSymbol; + public bool IsReadOnly; + public ITypeSymbol TypeSymbol; - // .ToString() assumes that the parameter type is IComponentData - public override string ToString() - { - return IsReadOnly - ? $"Unity.Entities.ComponentType.ReadOnly<{TypeSymbol.ToFullName()}>()" - : $"Unity.Entities.ComponentType.ReadWrite<{TypeSymbol.ToFullName()}>()"; - } - } - public partial class JobEntityDescription : ISourceGeneratorDiagnosable + // .ToString() assumes that the parameter type is IComponentData + public override string ToString() { - readonly List m_ComponentTypesInExecuteMethod; - readonly List m_AspectTypesInExecuteMethod; - readonly INamedTypeSymbol m_JobEntityTypeSymbol; - readonly bool m_CheckUserDefinedQueryForScheduling; + return IsReadOnly + ? $"Unity.Entities.ComponentType.ReadOnly<{TypeSymbol.ToFullName()}>()" + : $"Unity.Entities.ComponentType.ReadWrite<{TypeSymbol.ToFullName()}>()"; + } +} +public partial class JobEntityDescription : ISourceGeneratorDiagnosable +{ + readonly List m_ComponentTypesInExecuteMethod; + readonly List m_AspectTypesInExecuteMethod; + readonly INamedTypeSymbol m_JobEntityTypeSymbol; + readonly bool m_CheckUserDefinedQueryForScheduling; + readonly JobEntityParam[] _userExecuteMethodParams; - public List Diagnostics { get; } - public string FullTypeName => m_JobEntityTypeSymbol.ToFullName(); - string TypeName => m_JobEntityTypeSymbol.Name; - bool m_RequiresEntityManager; - bool HasManagedComponents => m_RequiresEntityManager; - bool m_HasEntityIndexInQuery; + public List SourceGenDiagnostics { get; } + string FullTypeName => m_JobEntityTypeSymbol.ToFullName(); + string TypeName => m_JobEntityTypeSymbol.Name; - public bool Invalid { get; private set; } - public readonly JobEntityParam[] UserExecuteMethodParams; + bool m_RequiresEntityManager; + bool m_HasEntityIndexInQuery; - HandlesDescription m_HandlesDescription; + public bool Invalid { get; private set; } - static string GetUserExecuteMethodSignature(IMethodSymbol userExecuteMethod) => $"{userExecuteMethod.Name}({userExecuteMethod.Parameters.Select(p => $"{p.Type.Name} {p.Name}").SeparateByComma()})"; + QueriesAndHandles _queriesAndHandles; - public JobEntityDescription(StructDeclarationSyntax candidate, SemanticModel semanticModel, bool checkUserDefinedQueryForScheduling) - { - Invalid = false; - Diagnostics = new List(); + static string GetUserExecuteMethodSignature(IMethodSymbol userExecuteMethod) => + $"{userExecuteMethod.Name}({userExecuteMethod.Parameters.Select(p => $"{p.Type.Name} {p.Name}").SeparateByComma()})"; - m_JobEntityTypeSymbol = semanticModel.GetDeclaredSymbol(candidate); - m_HandlesDescription = HandlesDescription.Create(candidate); - m_CheckUserDefinedQueryForScheduling = checkUserDefinedQueryForScheduling; + public JobEntityDescription(StructDeclarationSyntax candidate, SemanticModel semanticModel, bool checkUserDefinedQueryForScheduling) + { + Invalid = false; + SourceGenDiagnostics = new List(); - // Find valid user made execute method - can likely be shortened if we remove the SGJE0008 error :3 - var userExecuteMethods = new List(); - foreach (var member in m_JobEntityTypeSymbol.GetMembers()) - if (member is IMethodSymbol method && member.Name == "Execute") - userExecuteMethods.Add(method); + m_JobEntityTypeSymbol = semanticModel.GetDeclaredSymbol(candidate); + _queriesAndHandles = QueriesAndHandles.Create(candidate); + m_CheckUserDefinedQueryForScheduling = checkUserDefinedQueryForScheduling; - if (userExecuteMethods.Count != 1) - { - JobEntityGeneratorErrors.SGJE0008(this, m_JobEntityTypeSymbol.Locations.First(), FullTypeName, userExecuteMethods); - Invalid = true; - return; - } - var userExecuteMethod = userExecuteMethods[0]; + // Find valid user made execute method - can likely be shortened if we remove the SGJE0008 error :3 + var userExecuteMethods = new List(); + foreach (var member in m_JobEntityTypeSymbol.GetMembers()) + if (member is IMethodSymbol method && member.Name == "Execute") + userExecuteMethods.Add(method); - m_ComponentTypesInExecuteMethod = new List(); - m_AspectTypesInExecuteMethod = new List(); + if (userExecuteMethods.Count != 1) + { + JobEntityGeneratorErrors.SGJE0008(this, m_JobEntityTypeSymbol.Locations.First(), FullTypeName, userExecuteMethods); + Invalid = true; + return; + } + var userExecuteMethod = userExecuteMethods[0]; - // Generate JobEntityParams - var queryInfo = QueryInfo.Default(); - UserExecuteMethodParams = new JobEntityParam[userExecuteMethod.Parameters.Length]; + m_ComponentTypesInExecuteMethod = new List(); + m_AspectTypesInExecuteMethod = new List(); - for (var parameterIndex = 0; parameterIndex < userExecuteMethod.Parameters.Length; parameterIndex++) - UserExecuteMethodParams[parameterIndex] = CreateJobEntityParam(userExecuteMethod.Parameters[parameterIndex], checkUserDefinedQueryForScheduling, queryInfo.QueryAllTypes, queryInfo.QueryChangeFilterTypes); + // Generate JobEntityParams + var queryInfo = QueryInfo.Default(); + _userExecuteMethodParams = new JobEntityParam[userExecuteMethod.Parameters.Length]; - // Generate Query - queryInfo.FillFromAttributes(candidate.AttributeLists, semanticModel); - queryInfo.CreateDefaultQuery(ref m_HandlesDescription); + for (var parameterIndex = 0; parameterIndex < userExecuteMethod.Parameters.Length; parameterIndex++) + _userExecuteMethodParams[parameterIndex] = CreateJobEntityParam(userExecuteMethod.Parameters[parameterIndex], checkUserDefinedQueryForScheduling, queryInfo.QueryAllTypes, queryInfo.QueryChangeFilterTypes); - // Do remaining checks to see if this description is valid - OutputErrors(userExecuteMethod); - } + // Generate Query + queryInfo.FillFromAttributes(candidate.AttributeLists, semanticModel); + queryInfo.CreateDefaultQuery(ref _queriesAndHandles); - JobEntityParam CreateJobEntityParam(IParameterSymbol parameterSymbol, bool performSafetyChecks, - List queryAllTypes, List queryChangeFilterTypes) - { - var typeSymbol = parameterSymbol.Type; + // Do remaining checks to see if this description is valid + OutputErrors(userExecuteMethod); + } + + JobEntityParam CreateJobEntityParam(IParameterSymbol parameterSymbol, bool performSafetyChecks, + ICollection queryAllTypes, ICollection queryChangeFilterTypes) + { + var typeSymbol = parameterSymbol.Type; - var hasChangeFilter = false; - foreach (var attribute in parameterSymbol.GetAttributes()) + var hasChangeFilter = false; + foreach (var attribute in parameterSymbol.GetAttributes()) + { + switch (attribute.AttributeClass.ToFullName()) { - switch (attribute.AttributeClass.ToFullName()) - { - case "global::Unity.Entities.ChunkIndexInQuery": - return new JobEntityParam_ChunkIndexInQuery(parameterSymbol); - case "global::Unity.Entities.EntityIndexInChunk": - return new JobEntityParam_EntityIndexInChunk(parameterSymbol); - case "global::Unity.Entities.EntityIndexInQuery": - m_HasEntityIndexInQuery = true; - return new JobEntityParam_EntityIndexInQuery(parameterSymbol); - case "global::Unity.Entities.WithChangeFilterAttribute": - hasChangeFilter = true; - break; - } + case "global::Unity.Entities.ChunkIndexInQuery": + return new JobEntityParam_ChunkIndexInQuery(parameterSymbol); + case "global::Unity.Entities.EntityIndexInChunk": + return new JobEntityParam_EntityIndexInChunk(parameterSymbol); + case "global::Unity.Entities.EntityIndexInQuery": + m_HasEntityIndexInQuery = true; + return new JobEntityParam_EntityIndexInQuery(parameterSymbol); + case "global::Unity.Entities.WithChangeFilterAttribute": + hasChangeFilter = true; + break; } + } - switch (typeSymbol) + switch (typeSymbol) + { + case INamedTypeSymbol namedTypeSymbol: { - case INamedTypeSymbol namedTypeSymbol: + switch (namedTypeSymbol.Arity) { - switch (namedTypeSymbol.Arity) + case 1: { - case 1: - { - var typeArgSymbol = namedTypeSymbol.TypeArguments.Single(); - var fullName = namedTypeSymbol.ConstructedFrom.ToFullName(); - - switch (fullName) - { - // Dynamic Buffer - case "global::Unity.Entities.DynamicBuffer": - { - if (IsLessAccessibleThan(typeArgSymbol, m_JobEntityTypeSymbol)) - { - JobEntityGeneratorErrors.SGJE0023( - this, - parameterSymbol.Locations[0], - typeArgSymbol.ToFullName(), - Enum.GetName(typeof(Accessibility), typeArgSymbol.DeclaredAccessibility), - m_JobEntityTypeSymbol.ToFullName(), - Enum.GetName(typeof(Accessibility), m_JobEntityTypeSymbol.DeclaredAccessibility)); - Invalid = true; - return null; - } + var typeArgSymbol = namedTypeSymbol.TypeArguments.Single(); + var fullName = namedTypeSymbol.ConstructedFrom.ToFullName(); - var typeHandleName = m_HandlesDescription.GetOrCreateTypeHandleField(typeArgSymbol, parameterSymbol.IsReadOnly()); - var jobEntityParameter = new JobEntityParam_DynamicBuffer(parameterSymbol, typeArgSymbol, typeHandleName); - queryAllTypes.Add(new Query - { - IsReadOnly = jobEntityParameter.IsReadOnly, - Type = QueryType.All, - TypeSymbol = jobEntityParameter.TypeSymbol - }); - m_ComponentTypesInExecuteMethod.Add(new ParameterTypeInJobEntityExecuteMethod - { - IsReadOnly = jobEntityParameter.IsReadOnly, - TypeSymbol = jobEntityParameter.TypeSymbol, - }); - return jobEntityParameter; - } - - // RefX / EnabledRefX - case "global::Unity.Entities.RefRW": - case "global::Unity.Entities.RefRO": - case "global::Unity.Entities.EnabledRefRW": - case "global::Unity.Entities.EnabledRefRO": - { - if (typeArgSymbol.InheritsFromInterface("Unity.Entities.IComponentData")) - { - var isReadOnly = fullName == "global::Unity.Entities.RefRO" || - fullName == "global::Unity.Entities.EnabledRefRO"; - var typeHandleName = m_HandlesDescription.GetOrCreateTypeHandleField(typeArgSymbol, isReadOnly); - var (success, jobEntityParameter) = - JobEntityParam.TryParseComponentTypeSymbol( - typeArgSymbol, - parameterSymbol, - isReadOnly, - performSafetyChecks, - diagnosable: this, - typeHandleName, - constructedFrom: fullName); - - if (success) - { - if (IsLessAccessibleThan(typeArgSymbol, m_JobEntityTypeSymbol)) - { - JobEntityGeneratorErrors.SGJE0023( - this, - parameterSymbol.Locations[0], - typeArgSymbol.ToFullName(), - Enum.GetName(typeof(Accessibility), typeArgSymbol.DeclaredAccessibility), - m_JobEntityTypeSymbol.ToFullName(), - Enum.GetName(typeof(Accessibility), m_JobEntityTypeSymbol.DeclaredAccessibility)); - Invalid = true; - return null; - } - queryAllTypes.Add(new Query - { - IsReadOnly = jobEntityParameter.IsReadOnly, - Type = QueryType.All, - TypeSymbol = jobEntityParameter.TypeSymbol - }); - m_ComponentTypesInExecuteMethod.Add(new ParameterTypeInJobEntityExecuteMethod - { - IsReadOnly = jobEntityParameter.IsReadOnly, - TypeSymbol = jobEntityParameter.TypeSymbol, - }); - if (hasChangeFilter) - { - queryChangeFilterTypes.Add(new Query - { - IsReadOnly = jobEntityParameter.IsReadOnly, - Type = QueryType.ChangeFilter, - TypeSymbol = jobEntityParameter.TypeSymbol - }); - } - return jobEntityParameter; - } - - Invalid = true; - return null; - } - - Invalid = true; - JobEntityGeneratorErrors.SGJE0019(this, parameterSymbol.Locations.Single(), typeArgSymbol.ToFullName()); - return null; - } - default: - Invalid = true; - return null; - } - } - case 0: + switch (fullName) { - // Shared Components - if (typeSymbol.InheritsFromInterface("Unity.Entities.ISharedComponentData")) + // Dynamic Buffer + case "global::Unity.Entities.DynamicBuffer": { - if (IsLessAccessibleThan(typeSymbol, m_JobEntityTypeSymbol)) + if (IsLessAccessibleThan(typeArgSymbol, m_JobEntityTypeSymbol)) { JobEntityGeneratorErrors.SGJE0023( this, parameterSymbol.Locations[0], - typeSymbol.ToFullName(), - Enum.GetName(typeof(Accessibility), typeSymbol.DeclaredAccessibility), + typeArgSymbol.ToFullName(), + Enum.GetName(typeof(Accessibility), typeArgSymbol.DeclaredAccessibility), m_JobEntityTypeSymbol.ToFullName(), Enum.GetName(typeof(Accessibility), m_JobEntityTypeSymbol.DeclaredAccessibility)); Invalid = true; return null; } - var typeHandleName = m_HandlesDescription.GetOrCreateTypeHandleField(typeSymbol, parameterSymbol.IsReadOnly()); - m_RequiresEntityManager |= !typeSymbol.IsUnmanagedType; - var jobEntityParameter = new JobEntityParam_SharedComponent(parameterSymbol, typeHandleName); + var typeHandle = _queriesAndHandles.GetOrCreateTypeHandleField(typeArgSymbol, parameterSymbol.IsReadOnly()); + var jobEntityParameter = new JobEntityParam_DynamicBuffer(parameterSymbol, typeArgSymbol, typeHandle); queryAllTypes.Add(new Query { IsReadOnly = jobEntityParameter.IsReadOnly, @@ -255,54 +151,44 @@ JobEntityParam CreateJobEntityParam(IParameterSymbol parameterSymbol, bool perfo IsReadOnly = jobEntityParameter.IsReadOnly, TypeSymbol = jobEntityParameter.TypeSymbol, }); - if (hasChangeFilter) - { - queryChangeFilterTypes.Add(new Query - { - IsReadOnly = jobEntityParameter.IsReadOnly, - Type = QueryType.ChangeFilter, - TypeSymbol = jobEntityParameter.TypeSymbol - }); - } return jobEntityParameter; } - // Entity - if (typeSymbol.Is("Unity.Entities.Entity")) - { - var typeHandleName = m_HandlesDescription.GetOrCreateEntityTypeHandleField(); - return new JobEntityParam_Entity(parameterSymbol, typeHandleName); - } - if (typeSymbol.IsValueType) + // RefX / EnabledRefX + case "global::Unity.Entities.RefRW": + case "global::Unity.Entities.RefRO": + case "global::Unity.Entities.EnabledRefRW": + case "global::Unity.Entities.EnabledRefRO": { - // ComponentData - if (typeSymbol.InheritsFromInterface("Unity.Entities.IComponentData")) + if (typeArgSymbol.InheritsFromInterface("Unity.Entities.IComponentData")) { - var isReadOnly = parameterSymbol.IsReadOnly(); - var typeHandleName = m_HandlesDescription.GetOrCreateTypeHandleField(typeSymbol, isReadOnly); + var isReadOnly = fullName == "global::Unity.Entities.RefRO" || + fullName == "global::Unity.Entities.EnabledRefRO"; + var typeHandle = _queriesAndHandles.GetOrCreateTypeHandleField(typeArgSymbol, isReadOnly); var (success, jobEntityParameter) = JobEntityParam.TryParseComponentTypeSymbol( - typeSymbol, + typeArgSymbol, parameterSymbol, isReadOnly, performSafetyChecks, - diagnosable: this, typeHandleName); + diagnosable: this, + typeHandle, + constructedFrom: fullName); if (success) { - if (IsLessAccessibleThan(typeSymbol, m_JobEntityTypeSymbol)) + if (IsLessAccessibleThan(typeArgSymbol, m_JobEntityTypeSymbol)) { JobEntityGeneratorErrors.SGJE0023( this, parameterSymbol.Locations[0], - typeSymbol.ToFullName(), - Enum.GetName(typeof(Accessibility), typeSymbol.DeclaredAccessibility), + typeArgSymbol.ToFullName(), + Enum.GetName(typeof(Accessibility), typeArgSymbol.DeclaredAccessibility), m_JobEntityTypeSymbol.ToFullName(), Enum.GetName(typeof(Accessibility), m_JobEntityTypeSymbol.DeclaredAccessibility)); Invalid = true; return null; } - queryAllTypes.Add(new Query { IsReadOnly = jobEntityParameter.IsReadOnly, @@ -330,8 +216,82 @@ JobEntityParam CreateJobEntityParam(IParameterSymbol parameterSymbol, bool perfo return null; } - // Aspects - if (typeSymbol.IsAspect()) + Invalid = true; + JobEntityGeneratorErrors.SGJE0019(this, parameterSymbol.Locations.Single(), typeArgSymbol.ToFullName()); + return null; + } + default: + Invalid = true; + return null; + } + } + case 0: + { + // Shared Components + if (typeSymbol.InheritsFromInterface("Unity.Entities.ISharedComponentData")) + { + if (IsLessAccessibleThan(typeSymbol, m_JobEntityTypeSymbol)) + { + JobEntityGeneratorErrors.SGJE0023( + this, + parameterSymbol.Locations[0], + typeSymbol.ToFullName(), + Enum.GetName(typeof(Accessibility), typeSymbol.DeclaredAccessibility), + m_JobEntityTypeSymbol.ToFullName(), + Enum.GetName(typeof(Accessibility), m_JobEntityTypeSymbol.DeclaredAccessibility)); + Invalid = true; + return null; + } + + var typeHandle = _queriesAndHandles.GetOrCreateTypeHandleField(typeSymbol, parameterSymbol.IsReadOnly()); + m_RequiresEntityManager |= !typeSymbol.IsUnmanagedType; + var jobEntityParameter = new JobEntityParam_SharedComponent(parameterSymbol, typeHandle); + queryAllTypes.Add(new Query + { + IsReadOnly = jobEntityParameter.IsReadOnly, + Type = QueryType.All, + TypeSymbol = jobEntityParameter.TypeSymbol + }); + m_ComponentTypesInExecuteMethod.Add(new ParameterTypeInJobEntityExecuteMethod + { + IsReadOnly = jobEntityParameter.IsReadOnly, + TypeSymbol = jobEntityParameter.TypeSymbol, + }); + if (hasChangeFilter) + { + queryChangeFilterTypes.Add(new Query + { + IsReadOnly = jobEntityParameter.IsReadOnly, + Type = QueryType.ChangeFilter, + TypeSymbol = jobEntityParameter.TypeSymbol + }); + } + return jobEntityParameter; + } + + // Entity + if (typeSymbol.Is("Unity.Entities.Entity")) + { + var typeHandleName = _queriesAndHandles.GetOrCreateEntityTypeHandleField(); + return new JobEntityParam_Entity(parameterSymbol, typeHandleName); + } + if (typeSymbol.IsValueType) + { + // ComponentData + if (typeSymbol.InheritsFromInterface("Unity.Entities.IComponentData")) + { + var isReadOnly = parameterSymbol.IsReadOnly(); + var typeHandle = _queriesAndHandles.GetOrCreateTypeHandleField(typeSymbol, isReadOnly); + var (success, jobEntityParameter) = + JobEntityParam.TryParseComponentTypeSymbol( + typeSymbol, + parameterSymbol, + isReadOnly, + performSafetyChecks, + diagnosable: this, + typeHandle); + + if (success) { if (IsLessAccessibleThan(typeSymbol, m_JobEntityTypeSymbol)) { @@ -346,56 +306,36 @@ JobEntityParam CreateJobEntityParam(IParameterSymbol parameterSymbol, bool perfo return null; } - if (parameterSymbol.RefKind == RefKind.In || parameterSymbol.RefKind == RefKind.Ref - || parameterSymbol.RefKind == RefKind.RefReadOnly) - { - JobEntityGeneratorErrors.SGJE0021(this, parameterSymbol.Locations.Single(), typeSymbol.Name); - Invalid = true; - return null; - } - - var typeHandleName = m_HandlesDescription.GetOrCreateTypeHandleField(typeSymbol, parameterSymbol.IsReadOnly()); - var jobEntityParameter = new JobEntityParam_Aspect(parameterSymbol, typeHandleName); queryAllTypes.Add(new Query { IsReadOnly = jobEntityParameter.IsReadOnly, Type = QueryType.All, TypeSymbol = jobEntityParameter.TypeSymbol }); - m_AspectTypesInExecuteMethod.Add(new ParameterTypeInJobEntityExecuteMethod + m_ComponentTypesInExecuteMethod.Add(new ParameterTypeInJobEntityExecuteMethod { IsReadOnly = jobEntityParameter.IsReadOnly, TypeSymbol = jobEntityParameter.TypeSymbol, }); + if (hasChangeFilter) + { + queryChangeFilterTypes.Add(new Query + { + IsReadOnly = jobEntityParameter.IsReadOnly, + Type = QueryType.ChangeFilter, + TypeSymbol = jobEntityParameter.TypeSymbol + }); + } return jobEntityParameter; } - // Error handling - if (typeSymbol.InheritsFromInterface("Unity.Entities.IBufferElementData")) - { - JobEntityGeneratorErrors.SGJE0012(this, parameterSymbol.Locations.Single(), typeSymbol.Name); - Invalid = true; - return null; - } - - JobEntityGeneratorErrors.SGJE0003(this, parameterSymbol.Locations.Single(), parameterSymbol.Name, typeSymbol.ToFullName()); Invalid = true; - return new JobEntityParamValueTypesPassedWithDefaultArguments(parameterSymbol); + return null; } - // We are a reference type if we get here - if (typeSymbol.InheritsFromInterface("Unity.Entities.IComponentData") - || typeSymbol.InheritsFromType("UnityEngine.Component") - || typeSymbol.InheritsFromType("UnityEngine.GameObject") - || typeSymbol.InheritsFromType("UnityEngine.ScriptableObject")) + // Aspects + if (typeSymbol.IsAspect()) { - if (parameterSymbol.RefKind == RefKind.Ref || parameterSymbol.RefKind == RefKind.In) - { - JobEntityGeneratorErrors.SGJE0022(this, parameterSymbol.Locations.Single(), typeSymbol.Name); - Invalid = true; - return null; - } - if (IsLessAccessibleThan(typeSymbol, m_JobEntityTypeSymbol)) { JobEntityGeneratorErrors.SGJE0023( @@ -409,596 +349,732 @@ JobEntityParam CreateJobEntityParam(IParameterSymbol parameterSymbol, bool perfo return null; } - var typeHandleName = m_HandlesDescription.GetOrCreateTypeHandleField(typeSymbol, parameterSymbol.IsReadOnly()); - m_RequiresEntityManager = true; - var jobEntityParameter = new JobEntityParam_ManagedComponent(parameterSymbol, typeHandleName); + if (parameterSymbol.RefKind == RefKind.In || parameterSymbol.RefKind == RefKind.Ref + || parameterSymbol.RefKind == RefKind.RefReadOnly) + { + JobEntityGeneratorErrors.SGJE0021(this, parameterSymbol.Locations.Single(), typeSymbol.Name); + Invalid = true; + return null; + } + + var typeHandle = _queriesAndHandles.GetOrCreateTypeHandleField(typeSymbol, parameterSymbol.IsReadOnly()); + var jobEntityParameter = new JobEntityParam_Aspect(parameterSymbol, typeHandle); queryAllTypes.Add(new Query { IsReadOnly = jobEntityParameter.IsReadOnly, Type = QueryType.All, TypeSymbol = jobEntityParameter.TypeSymbol }); - m_ComponentTypesInExecuteMethod.Add(new ParameterTypeInJobEntityExecuteMethod + m_AspectTypesInExecuteMethod.Add(new ParameterTypeInJobEntityExecuteMethod { IsReadOnly = jobEntityParameter.IsReadOnly, TypeSymbol = jobEntityParameter.TypeSymbol, }); - if (hasChangeFilter) - { - queryChangeFilterTypes.Add(new Query - { - IsReadOnly = jobEntityParameter.IsReadOnly, - Type = QueryType.ChangeFilter, - TypeSymbol = jobEntityParameter.TypeSymbol - }); - } return jobEntityParameter; } - JobEntityGeneratorErrors.SGJE0010(this, parameterSymbol.Locations.Single(), parameterSymbol.Name, typeSymbol.ToFullName()); + // Error handling + if (typeSymbol.InheritsFromInterface("Unity.Entities.IBufferElementData")) + { + JobEntityGeneratorErrors.SGJE0012(this, parameterSymbol.Locations.Single(), typeSymbol.Name); + Invalid = true; + return null; + } + + JobEntityGeneratorErrors.SGJE0003(this, parameterSymbol.Locations.Single(), parameterSymbol.Name, typeSymbol.ToFullName()); Invalid = true; - return null; + return new JobEntityParamValueTypesPassedWithDefaultArguments(parameterSymbol); } - default: + + // We are a reference type if we get here + if (typeSymbol.InheritsFromInterface("Unity.Entities.IComponentData") + || typeSymbol.InheritsFromType("UnityEngine.Component") + || typeSymbol.InheritsFromType("UnityEngine.GameObject") + || typeSymbol.InheritsFromType("UnityEngine.ScriptableObject")) { - Invalid = true; - return null; + if (parameterSymbol.RefKind == RefKind.Ref || parameterSymbol.RefKind == RefKind.In) + { + JobEntityGeneratorErrors.SGJE0022(this, parameterSymbol.Locations.Single(), typeSymbol.Name); + Invalid = true; + return null; + } + + if (IsLessAccessibleThan(typeSymbol, m_JobEntityTypeSymbol)) + { + JobEntityGeneratorErrors.SGJE0023( + this, + parameterSymbol.Locations[0], + typeSymbol.ToFullName(), + Enum.GetName(typeof(Accessibility), typeSymbol.DeclaredAccessibility), + m_JobEntityTypeSymbol.ToFullName(), + Enum.GetName(typeof(Accessibility), m_JobEntityTypeSymbol.DeclaredAccessibility)); + Invalid = true; + return null; + } + + var typeHandle = _queriesAndHandles.GetOrCreateTypeHandleField(typeSymbol, parameterSymbol.IsReadOnly()); + m_RequiresEntityManager = true; + var jobEntityParameter = new JobEntityParam_ManagedComponent(parameterSymbol, typeHandle); + queryAllTypes.Add(new Query + { + IsReadOnly = jobEntityParameter.IsReadOnly, + Type = QueryType.All, + TypeSymbol = jobEntityParameter.TypeSymbol + }); + m_ComponentTypesInExecuteMethod.Add(new ParameterTypeInJobEntityExecuteMethod + { + IsReadOnly = jobEntityParameter.IsReadOnly, + TypeSymbol = jobEntityParameter.TypeSymbol, + }); + if (hasChangeFilter) + { + queryChangeFilterTypes.Add(new Query + { + IsReadOnly = jobEntityParameter.IsReadOnly, + Type = QueryType.ChangeFilter, + TypeSymbol = jobEntityParameter.TypeSymbol + }); + } + return jobEntityParameter; } + + JobEntityGeneratorErrors.SGJE0010(this, parameterSymbol.Locations.Single(), parameterSymbol.Name, typeSymbol.ToFullName()); + Invalid = true; + return null; + } + default: + { + Invalid = true; + return null; } } } - Invalid = true; - return null; } + Invalid = true; + return null; + } - ref struct QueryInfo - { - public List QueryAllTypes; - public List QueryAnyTypes; - public List QueryNoneTypes; - public List QueryDisabledTypes; - public List QueryAbsentTypes; - public List QueryChangeFilterTypes; - public EntityQueryOptions EntityQueryOptions; - - public static QueryInfo Default() => - new QueryInfo - { - QueryAllTypes = new List(), - QueryAnyTypes = new List(), - QueryNoneTypes = new List(), - QueryDisabledTypes = new List(), - QueryAbsentTypes = new List(), - QueryChangeFilterTypes = new List(), - EntityQueryOptions = EntityQueryOptions.Default, - }; - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - static ITypeSymbol AddFromTypeOfKeepAll(List list, TypeOfExpressionSyntax typeOfSyntax, QueryType type, SemanticModel semanticModel) + ref struct QueryInfo + { + public List QueryAllTypes; + public List QueryAnyTypes; + public List QueryNoneTypes; + public List QueryDisabledTypes; + public List QueryAbsentTypes; + public List QueryPresentTypes; + public List QueryChangeFilterTypes; + public EntityQueryOptions EntityQueryOptions; + + public static QueryInfo Default() => + new() { - var typeSymbol = semanticModel.GetTypeInfo(typeOfSyntax.Type).Type; - list.Add(new Query - { - TypeSymbol = typeSymbol, - Type = type, - IsReadOnly = true, - }); - return typeSymbol; - } + QueryAllTypes = new List(), + QueryAnyTypes = new List(), + QueryNoneTypes = new List(), + QueryDisabledTypes = new List(), + QueryAbsentTypes = new List(), + QueryPresentTypes = new List(), + QueryChangeFilterTypes = new List(), + EntityQueryOptions = EntityQueryOptions.Default, + }; - [MethodImpl(MethodImplOptions.AggressiveInlining)] - void AddFromTypeOfRemoveFromAll(List list, TypeOfExpressionSyntax typeOfSyntax, QueryType type, SemanticModel semanticModel) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + static ITypeSymbol AddFromTypeOfKeepAll(List list, TypeOfExpressionSyntax typeOfSyntax, QueryType type, SemanticModel semanticModel) + { + var typeSymbol = semanticModel.GetTypeInfo(typeOfSyntax.Type).Type; + list.Add(new Query { - var typeSymbol = AddFromTypeOfKeepAll(list, typeOfSyntax, type, semanticModel); - var index = QueryAllTypes.FindIndex(q => SymbolEqualityComparer.Default.Equals(q.TypeSymbol, typeSymbol)); - if (index != -1) - QueryAllTypes.RemoveAtSwapBack(index); - } + TypeSymbol = typeSymbol, + Type = type, + IsReadOnly = true, + }); + return typeSymbol; + } - static void AddToQueryListKeepAll(List list, AttributeArgumentSyntax argument, QueryType type, SemanticModel semanticModel) + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void AddFromTypeOfRemoveFromAll(List list, TypeOfExpressionSyntax typeOfSyntax, QueryType type, SemanticModel semanticModel) + { + var typeSymbol = AddFromTypeOfKeepAll(list, typeOfSyntax, type, semanticModel); + var index = QueryAllTypes.FindIndex(q => SymbolEqualityComparer.Default.Equals(q.TypeSymbol, typeSymbol)); + if (index != -1) + QueryAllTypes.RemoveAtSwapBack(index); + } + + static void AddToQueryListKeepAll(List list, AttributeArgumentSyntax argument, QueryType type, SemanticModel semanticModel) + { + switch (argument.Expression) { - switch (argument.Expression) - { - case ImplicitArrayCreationExpressionSyntax implicitArraySyntax: - foreach (var expression in implicitArraySyntax.Initializer.Expressions) - if (expression is TypeOfExpressionSyntax typeOfSyntax) - AddFromTypeOfKeepAll(list, typeOfSyntax, type, semanticModel); - break; - case ArrayCreationExpressionSyntax arraySyntax: - foreach (var expression in arraySyntax.Initializer.Expressions) - if (expression is TypeOfExpressionSyntax typeOfSyntax) - AddFromTypeOfKeepAll(list, typeOfSyntax, type, semanticModel); - break; - case TypeOfExpressionSyntax typeOfSyntax: - AddFromTypeOfKeepAll(list, typeOfSyntax, type, semanticModel); - break; - } + case ImplicitArrayCreationExpressionSyntax implicitArraySyntax: + foreach (var expression in implicitArraySyntax.Initializer.Expressions) + if (expression is TypeOfExpressionSyntax typeOfSyntax) + AddFromTypeOfKeepAll(list, typeOfSyntax, type, semanticModel); + break; + case ArrayCreationExpressionSyntax arraySyntax: + foreach (var expression in arraySyntax.Initializer.Expressions) + if (expression is TypeOfExpressionSyntax typeOfSyntax) + AddFromTypeOfKeepAll(list, typeOfSyntax, type, semanticModel); + break; + case TypeOfExpressionSyntax typeOfSyntax: + AddFromTypeOfKeepAll(list, typeOfSyntax, type, semanticModel); + break; } + } - void AddToQueryListRemoveFromAll(List list, AttributeArgumentSyntax argument, QueryType type, SemanticModel semanticModel) + void AddToQueryListRemoveFromAll(List list, AttributeArgumentSyntax argument, QueryType type, SemanticModel semanticModel) + { + switch (argument.Expression) { - switch (argument.Expression) - { - case ImplicitArrayCreationExpressionSyntax implicitArraySyntax: - foreach (var expression in implicitArraySyntax.Initializer.Expressions) - if (expression is TypeOfExpressionSyntax typeOfSyntax) - AddFromTypeOfRemoveFromAll(list, typeOfSyntax, type, semanticModel); - break; - case ArrayCreationExpressionSyntax arraySyntax: - foreach (var expression in arraySyntax.Initializer.Expressions) - if (expression is TypeOfExpressionSyntax typeOfSyntax) - AddFromTypeOfRemoveFromAll(list, typeOfSyntax, type, semanticModel); - break; - case TypeOfExpressionSyntax typeOfSyntax: - AddFromTypeOfRemoveFromAll(list, typeOfSyntax, type, semanticModel); - break; - } + case ImplicitArrayCreationExpressionSyntax implicitArraySyntax: + foreach (var expression in implicitArraySyntax.Initializer.Expressions) + if (expression is TypeOfExpressionSyntax typeOfSyntax) + AddFromTypeOfRemoveFromAll(list, typeOfSyntax, type, semanticModel); + break; + case ArrayCreationExpressionSyntax arraySyntax: + foreach (var expression in arraySyntax.Initializer.Expressions) + if (expression is TypeOfExpressionSyntax typeOfSyntax) + AddFromTypeOfRemoveFromAll(list, typeOfSyntax, type, semanticModel); + break; + case TypeOfExpressionSyntax typeOfSyntax: + AddFromTypeOfRemoveFromAll(list, typeOfSyntax, type, semanticModel); + break; } + } - /// - /// Fills , , - /// , - /// and using C# attributes found on the JobEntity struct. - /// - public void FillFromAttributes(SyntaxList attributeLists, SemanticModel semanticModel) + /// + /// Fills , , + /// , + /// and using C# attributes found on the JobEntity struct. + /// + public void FillFromAttributes(SyntaxList attributeLists, SemanticModel semanticModel) + { + // Attributes contain multiple lists + // [MyAttributeA, MyAttributeB] [MyAttributeC] [MyAttributeD] + foreach (var attributeList in attributeLists) { - // Attributes contain multiple lists - // [MyAttributeA, MyAttributeB] [MyAttributeC] [MyAttributeD] - foreach (var attributeList in attributeLists) + // With multiple attributes + // [MyAttributeA, MyAttributeB] + foreach (var attribute in attributeList.Attributes) { - // With multiple attributes - // [MyAttributeA, MyAttributeB] - foreach (var attribute in attributeList.Attributes) + switch (attribute.Name) { - switch (attribute.Name) - { - case SimpleNameSyntax { Identifier: { ValueText: "WithDisabled" } }: - if (attribute.ArgumentList != null) - foreach (var argument in attribute.ArgumentList.Arguments) - AddToQueryListRemoveFromAll(QueryDisabledTypes, argument, QueryType.Disabled, semanticModel); - break; - case SimpleNameSyntax { Identifier: { ValueText: "WithAbsent" } }: - if (attribute.ArgumentList != null) - foreach (var argument in attribute.ArgumentList.Arguments) - AddToQueryListKeepAll(QueryAbsentTypes, argument, QueryType.Absent, semanticModel); - break; - case SimpleNameSyntax { Identifier: { ValueText: "WithAll" } }: - if (attribute.ArgumentList != null) - foreach (var argument in attribute.ArgumentList.Arguments) - AddToQueryListKeepAll(QueryAllTypes, argument, QueryType.All, semanticModel); - break; - case SimpleNameSyntax { Identifier: { ValueText: "WithNone" } }: - if (attribute.ArgumentList != null) - foreach (var argument in attribute.ArgumentList.Arguments) - AddToQueryListKeepAll(QueryNoneTypes, argument, QueryType.None, semanticModel); - break; - - case SimpleNameSyntax { Identifier: { ValueText: "WithAny" } }: - if (attribute.ArgumentList != null) - foreach (var argument in attribute.ArgumentList.Arguments) - AddToQueryListKeepAll(QueryAnyTypes, argument, QueryType.Any, semanticModel); - break; - - case SimpleNameSyntax { Identifier: { ValueText: "WithChangeFilter" } }: - if (attribute.ArgumentList != null) - foreach (var argument in attribute.ArgumentList.Arguments) - AddToQueryListKeepAll(QueryChangeFilterTypes, argument, QueryType.ChangeFilter, semanticModel); - break; - - case SimpleNameSyntax { Identifier: { ValueText: "WithOptions" } }: - if (attribute.ArgumentList != null) - foreach (var argument in attribute.ArgumentList.Arguments) - AddEntityQueryOption(argument.Expression); - break; - } + case SimpleNameSyntax { Identifier.ValueText: "WithDisabled" }: + if (attribute.ArgumentList != null) + foreach (var argument in attribute.ArgumentList.Arguments) + AddToQueryListRemoveFromAll(QueryDisabledTypes, argument, QueryType.Disabled, semanticModel); + break; + case SimpleNameSyntax { Identifier.ValueText: "WithPresent" }: + if (attribute.ArgumentList != null) + foreach (var argument in attribute.ArgumentList.Arguments) + AddToQueryListKeepAll(QueryPresentTypes, argument, QueryType.Present, semanticModel); + break; + case SimpleNameSyntax { Identifier.ValueText: "WithAbsent" }: + if (attribute.ArgumentList != null) + foreach (var argument in attribute.ArgumentList.Arguments) + AddToQueryListKeepAll(QueryAbsentTypes, argument, QueryType.Absent, semanticModel); + break; + case SimpleNameSyntax { Identifier.ValueText: "WithAll" }: + if (attribute.ArgumentList != null) + foreach (var argument in attribute.ArgumentList.Arguments) + AddToQueryListKeepAll(QueryAllTypes, argument, QueryType.All, semanticModel); + break; + case SimpleNameSyntax { Identifier.ValueText: "WithNone" }: + if (attribute.ArgumentList != null) + foreach (var argument in attribute.ArgumentList.Arguments) + AddToQueryListKeepAll(QueryNoneTypes, argument, QueryType.None, semanticModel); + break; + case SimpleNameSyntax { Identifier.ValueText: "WithAny" }: + if (attribute.ArgumentList != null) + foreach (var argument in attribute.ArgumentList.Arguments) + AddToQueryListKeepAll(QueryAnyTypes, argument, QueryType.Any, semanticModel); + break; + case SimpleNameSyntax { Identifier.ValueText: "WithChangeFilter" }: + if (attribute.ArgumentList != null) + foreach (var argument in attribute.ArgumentList.Arguments) + AddToQueryListKeepAll(QueryChangeFilterTypes, argument, QueryType.ChangeFilter, semanticModel); + break; + case SimpleNameSyntax { Identifier.ValueText: "WithOptions" }: + if (attribute.ArgumentList != null) + foreach (var argument in attribute.ArgumentList.Arguments) + AddEntityQueryOption(argument.Expression); + break; } } } + } - void AddEntityQueryOption(ExpressionSyntax expression) + void AddEntityQueryOption(ExpressionSyntax expression) + { + switch (expression) { - switch (expression) - { - // if bitwise or - case BinaryExpressionSyntax binaryExpression - when binaryExpression.IsKind(SyntaxKind.BitwiseOrExpression): - if (binaryExpression.Right is MemberAccessExpressionSyntax memberAccess) - EntityQueryOptions |= memberAccess.GetEntityQueryOptions(); - AddEntityQueryOption(binaryExpression.Left); - break; - case MemberAccessExpressionSyntax memberAccessSyntax: - EntityQueryOptions |= memberAccessSyntax.GetEntityQueryOptions(); - break; - } + // if bitwise or + case BinaryExpressionSyntax binaryExpression + when binaryExpression.IsKind(SyntaxKind.BitwiseOrExpression): + if (binaryExpression.Right is MemberAccessExpressionSyntax memberAccess) + EntityQueryOptions |= GetEntityQueryOptions(memberAccess); + AddEntityQueryOption(binaryExpression.Left); + break; + case MemberAccessExpressionSyntax memberAccessSyntax: + EntityQueryOptions |= GetEntityQueryOptions(memberAccessSyntax); + break; } - public void CreateDefaultQuery(ref HandlesDescription handlesDescription) + static EntityQueryOptions GetEntityQueryOptions(MemberAccessExpressionSyntax expression) { - handlesDescription.GetOrCreateQueryField( - new SingleArchetypeQueryFieldDescription( - new Archetype(QueryAllTypes, - QueryAnyTypes, - QueryNoneTypes, - QueryDisabledTypes, - QueryAbsentTypes, - options: EntityQueryOptions), - changeFilterTypes: QueryChangeFilterTypes), "DefaultQuery"); + return expression.Name.Identifier.ValueText switch + { + "Default" => EntityQueryOptions.Default, + "IncludePrefab" => EntityQueryOptions.IncludePrefab, + "IncludeDisabledEntities" => EntityQueryOptions.IncludeDisabledEntities, + "FilterWriteGroup" => EntityQueryOptions.FilterWriteGroup, + "IgnoreComponentEnabledState" => EntityQueryOptions.IgnoreComponentEnabledState, + "IncludeSystems" => EntityQueryOptions.IncludeSystems, + _ => throw new ArgumentOutOfRangeException() + }; } } - /// - /// Checks if any errors are present, if so, also outputs the error. - /// - /// - /// True if error found - void OutputErrors(IMethodSymbol userExecuteMethod) + public void CreateDefaultQuery(ref QueriesAndHandles queriesAndHandles) { - var foundChunkIndexInQuery = false; - var foundEntityIndexInQuery = false; - var foundEntityIndexInChunk = false; + queriesAndHandles.GetOrCreateQueryField( + new SingleArchetypeQueryFieldDescription( + new Archetype(QueryAllTypes, + QueryAnyTypes, + QueryNoneTypes, + QueryDisabledTypes, + QueryAbsentTypes, + QueryPresentTypes, + options: EntityQueryOptions), + changeFilterTypes: QueryChangeFilterTypes), + "DefaultQuery"); + } + } - // Check to see if our job has any generic type parameters - if (m_JobEntityTypeSymbol.TypeParameters.Any()) + readonly struct ExecuteMethodParameter : IEqualityComparer + { + readonly ITypeSymbol _typeSymbol; + readonly ComponentRefWrapperType _componentRefWrapperType; + + public ExecuteMethodParameter(ITypeSymbol typeSymbol, ComponentRefWrapperType refWrapperType) + { + _typeSymbol = typeSymbol; + _componentRefWrapperType = refWrapperType; + } + public bool Equals(ExecuteMethodParameter x, ExecuteMethodParameter y) + { + var identicalTypes = SymbolEqualityComparer.Default.Equals(x._typeSymbol, y._typeSymbol); + if (!identicalTypes) + return false; + + switch (x._componentRefWrapperType) { - JobEntityGeneratorErrors.SGJE0020(this, m_JobEntityTypeSymbol.Locations.First(), FullTypeName); - Invalid = true; + case ComponentRefWrapperType.NotApplicable: + return true; + case ComponentRefWrapperType.None: + case ComponentRefWrapperType.RefRO: + case ComponentRefWrapperType.RefRW: + return y._componentRefWrapperType is ComponentRefWrapperType.RefRO or ComponentRefWrapperType.RefRW or ComponentRefWrapperType.None; + case ComponentRefWrapperType.EnabledRefRO: + case ComponentRefWrapperType.EnabledRefRW: + default: + return y._componentRefWrapperType is ComponentRefWrapperType.EnabledRefRO or ComponentRefWrapperType.EnabledRefRW; } + } + + public int GetHashCode(ExecuteMethodParameter obj) + { + unchecked + { + int refWrapperTypeInt = _componentRefWrapperType switch + { + ComponentRefWrapperType.NotApplicable => 0, + ComponentRefWrapperType.None => 1, + ComponentRefWrapperType.RefRO => 1, + ComponentRefWrapperType.RefRW => 1, + ComponentRefWrapperType.EnabledRefRO => 2, + ComponentRefWrapperType.EnabledRefRW => 2, + _ => 2 + }; + return ((_typeSymbol != null ? SymbolEqualityComparer.Default.GetHashCode(_typeSymbol) : 0) * 397) ^ refWrapperTypeInt; + } + } + } + + /// + /// Checks if any errors are present, if so, also outputs the error. + /// + /// + /// True if error found + void OutputErrors(IMethodSymbol userExecuteMethod) + { + var foundChunkIndexInQuery = false; + var foundEntityIndexInQuery = false; + var foundEntityIndexInChunk = false; + + // Check to see if our job has any generic type parameters + if (m_JobEntityTypeSymbol.TypeParameters.Any()) + { + JobEntityGeneratorErrors.SGJE0020(this, m_JobEntityTypeSymbol.Locations.First(), FullTypeName); + Invalid = true; + } // TODO: This was recently fixed (https://github.com/dotnet/roslyn-analyzers/issues/5804), remove pragmas after we update .net #pragma warning disable RS1024 - var alreadySeenComponents = new HashSet(SymbolEqualityComparer.Default); + var executeMethodComponentParameters = new HashSet(); #pragma warning restore RS1024 - foreach (var param in UserExecuteMethodParams) - { - if (param is null) - continue; + foreach (var param in _userExecuteMethodParams) + { + if (param is null) + continue; - if (!(param is IAttributeParameter { IsInt: true })) - if (!alreadySeenComponents.Add(param.TypeSymbol)) - { - JobEntityGeneratorErrors.SGJE0017(this, param.ParameterSymbol.Locations.First(), FullTypeName, param.TypeSymbol.ToDisplayString()); - Invalid = true; - } + if (param is not IAttributeParameter { IsInt: true }) + { + var refWrapperType = param is JobEntityParam_ComponentData componentData ? componentData.ComponentRefWrapperType : ComponentRefWrapperType.NotApplicable; - switch (param) + if (!executeMethodComponentParameters.Add(new ExecuteMethodParameter(param.TypeSymbol, refWrapperType))) { - case IAttributeParameter attributeParameter: - { - if (attributeParameter.IsInt) { - switch (attributeParameter) - { - case JobEntityParam_ChunkIndexInQuery _ when !foundChunkIndexInQuery: - foundChunkIndexInQuery = true; - continue; - case JobEntityParam_EntityIndexInQuery _ when !foundEntityIndexInQuery: - foundEntityIndexInQuery = true; - continue; - case JobEntityParam_EntityIndexInChunk _ when !foundEntityIndexInChunk: - foundEntityIndexInChunk = true; - continue; - } + JobEntityGeneratorErrors.SGJE0017(this, param.ParameterSymbol.Locations.First(), FullTypeName, param.TypeSymbol.ToDisplayString()); + Invalid = true; + } + } - JobEntityGeneratorErrors.SGJE0007(this, param.ParameterSymbol.Locations.Single(), - FullTypeName, GetUserExecuteMethodSignature(userExecuteMethod), attributeParameter.AttributeName); - Invalid = true; - continue; + switch (param) + { + case IAttributeParameter attributeParameter: + { + if (attributeParameter.IsInt) { + switch (attributeParameter) + { + case JobEntityParam_ChunkIndexInQuery _ when !foundChunkIndexInQuery: + foundChunkIndexInQuery = true; + continue; + case JobEntityParam_EntityIndexInQuery _ when !foundEntityIndexInQuery: + foundEntityIndexInQuery = true; + continue; + case JobEntityParam_EntityIndexInChunk _ when !foundEntityIndexInChunk: + foundEntityIndexInChunk = true; + continue; } - JobEntityGeneratorErrors.SGJE0006(this, param.ParameterSymbol.Locations.Single(), - FullTypeName, GetUserExecuteMethodSignature(userExecuteMethod), param.ParameterSymbol.Name, attributeParameter.AttributeName); + JobEntityGeneratorErrors.SGJE0007(this, param.ParameterSymbol.Locations.Single(), + FullTypeName, GetUserExecuteMethodSignature(userExecuteMethod), attributeParameter.AttributeName); Invalid = true; continue; } - case JobEntityParam_SharedComponent sharedComponent: - if (sharedComponent.ParameterSymbol.RefKind == RefKind.Ref) - { - var text = sharedComponent.ParameterSymbol.DeclaringSyntaxReferences.First().GetSyntax() is ParameterSyntax {Identifier: var i} - ? i.ValueText - : sharedComponent.ParameterSymbol.ToDisplayString(); - JobEntityGeneratorErrors.SGJE0013(this, sharedComponent.ParameterSymbol.Locations.Single(), FullTypeName, text); - Invalid = true; - } - break; - case JobEntityParam_ComponentData componentData: + + JobEntityGeneratorErrors.SGJE0006(this, param.ParameterSymbol.Locations.Single(), + FullTypeName, GetUserExecuteMethodSignature(userExecuteMethod), param.ParameterSymbol.Name, attributeParameter.AttributeName); + Invalid = true; + continue; + } + case JobEntityParam_SharedComponent sharedComponent: + if (sharedComponent.ParameterSymbol.RefKind == RefKind.Ref) { - // E.g. Execute(in RefRW t1, ref EnabledRefRO t2) - if (componentData.RefWrapperType != RefWrapperType.None - && componentData.ParameterSymbol.RefKind != RefKind.None) - { - JobEntityGeneratorErrors.SGJE0018(this,componentData.ParameterSymbol.Locations.Single()); - Invalid = true; - } + var text = sharedComponent.ParameterSymbol.DeclaringSyntaxReferences.First().GetSyntax() is ParameterSyntax {Identifier: var i} + ? i.ValueText + : sharedComponent.ParameterSymbol.ToDisplayString(); + JobEntityGeneratorErrors.SGJE0013(this, sharedComponent.ParameterSymbol.Locations.Single(), FullTypeName, text); + Invalid = true; + } + break; + case JobEntityParam_ComponentData componentData: + { + // E.g. Execute(in RefRW t1, ref EnabledRefRO t2) + if (componentData.ComponentRefWrapperType != ComponentRefWrapperType.None + && componentData.ParameterSymbol.RefKind != RefKind.None) + { + JobEntityGeneratorErrors.SGJE0018(this,componentData.ParameterSymbol.Locations.Single()); + Invalid = true; + } - // E.g. Execute(ref TagComponent tag) - else if (componentData.RefWrapperType == RefWrapperType.None - && componentData.IsZeroSizedComponent - && componentData.ParameterSymbol.RefKind == RefKind.Ref) - { - JobEntityGeneratorErrors.SGJE0016( - this, - componentData.ParameterSymbol.Locations.Single(), - FullTypeName, - componentData.ParameterSymbol.ToDisplayString()); - Invalid = true; - } - break; + // E.g. Execute(ref TagComponent tag) + else if (componentData.ComponentRefWrapperType == ComponentRefWrapperType.None + && componentData.IsZeroSizedComponent + && componentData.ParameterSymbol.RefKind == RefKind.Ref) + { + JobEntityGeneratorErrors.SGJE0016( + this, + componentData.ParameterSymbol.Locations.Single(), + FullTypeName, + componentData.ParameterSymbol.ToDisplayString()); + Invalid = true; } + break; } } } } +} - public class JobEntityParam_SharedComponent : JobEntityParam +public class JobEntityParam_SharedComponent : JobEntityParam +{ + internal JobEntityParam_SharedComponent(IParameterSymbol parameterSymbol, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) { - internal JobEntityParam_SharedComponent(IParameterSymbol parameterSymbol, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) - { - var typeName = TypeSymbol.Name; - var variableName = $"{parameterSymbol.Name}Data"; - VariableDeclarationAtStartOfExecuteMethod = TypeSymbol.IsUnmanagedType - ? $"var {variableName} = chunk.GetSharedComponent(__TypeHandle.{TypeHandleFieldName});" - : $"var {variableName} = chunk.GetSharedComponentManaged(__TypeHandle.{TypeHandleFieldName}, __EntityManager);"; + var typeName = TypeSymbol.Name; + var variableName = $"{parameterSymbol.Name}Data"; + VariableDeclarationAtStartOfExecuteMethod = TypeSymbol.IsUnmanagedType + ? $"var {variableName} = chunk.GetSharedComponent(__TypeHandle.{TypeHandleFieldName});" + : $"var {variableName} = chunk.GetSharedComponentManaged(__TypeHandle.{TypeHandleFieldName}, __EntityManager);"; - ExecuteMethodArgumentValue = parameterSymbol.RefKind == RefKind.In ? $"in {variableName}" : variableName; - } + ExecuteMethodArgumentValue = parameterSymbol.RefKind == RefKind.In ? $"in {variableName}" : variableName; } +} - public class JobEntityParam_Entity : JobEntityParam +public class JobEntityParam_Entity : JobEntityParam +{ + internal JobEntityParam_Entity(IParameterSymbol parameterSymbol, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) { - internal JobEntityParam_Entity(IParameterSymbol parameterSymbol, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) - { - const string entityArrayPointer = "entityPointer"; - VariableDeclarationAtStartOfExecuteMethod = $@"var {entityArrayPointer} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkEntityArrayIntPtr(chunk, __TypeHandle.{TypeHandleFieldName});"; + const string entityArrayPointer = "entityPointer"; + VariableDeclarationAtStartOfExecuteMethod = $@"var {entityArrayPointer} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkEntityArrayIntPtr(chunk, __TypeHandle.{TypeHandleFieldName});"; - const string argumentInExecuteMethod = "entity"; - ExecuteMethodArgumentSetup = $"var {argumentInExecuteMethod} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement({entityArrayPointer}, entityIndexInChunk);"; + const string argumentInExecuteMethod = "entity"; + ExecuteMethodArgumentSetup = $"var {argumentInExecuteMethod} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement({entityArrayPointer}, entityIndexInChunk);"; - ExecuteMethodArgumentValue = argumentInExecuteMethod; - } + ExecuteMethodArgumentValue = argumentInExecuteMethod; } +} - public class JobEntityParam_DynamicBuffer : JobEntityParam +public class JobEntityParam_DynamicBuffer : JobEntityParam +{ + internal JobEntityParam_DynamicBuffer(IParameterSymbol parameterSymbol, ITypeSymbol typeArgSymbol, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) { - internal JobEntityParam_DynamicBuffer(IParameterSymbol parameterSymbol, ITypeSymbol typeArgSymbol, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) - { - TypeSymbol = typeArgSymbol; + TypeSymbol = typeArgSymbol; - var bufferAccessorVariableName = $"{parameterSymbol.Name}BufferAccessor"; - VariableDeclarationAtStartOfExecuteMethod = $"var {bufferAccessorVariableName} = chunk.GetBufferAccessor(ref __TypeHandle.{TypeHandleFieldName});"; + var bufferAccessorVariableName = $"{parameterSymbol.Name}BufferAccessor"; + VariableDeclarationAtStartOfExecuteMethod = $"var {bufferAccessorVariableName} = chunk.GetBufferAccessor(ref __TypeHandle.{TypeHandleFieldName});"; - var executeArgumentName = $"retrievedByIndexIn{bufferAccessorVariableName}"; - ExecuteMethodArgumentSetup = $"var {executeArgumentName} = {bufferAccessorVariableName}[entityIndexInChunk];"; + var executeArgumentName = $"retrievedByIndexIn{bufferAccessorVariableName}"; + ExecuteMethodArgumentSetup = $"var {executeArgumentName} = {bufferAccessorVariableName}[entityIndexInChunk];"; - ExecuteMethodArgumentValue = parameterSymbol.RefKind switch - { - RefKind.Ref => $"ref {executeArgumentName}", - RefKind.In => $"in {executeArgumentName}", - _ => executeArgumentName - }; - } + ExecuteMethodArgumentValue = parameterSymbol.RefKind switch + { + RefKind.Ref => $"ref {executeArgumentName}", + RefKind.In => $"in {executeArgumentName}", + _ => executeArgumentName + }; } +} - public class JobEntityParam_Aspect : JobEntityParam +public class JobEntityParam_Aspect : JobEntityParam +{ + internal JobEntityParam_Aspect(IParameterSymbol parameterSymbol, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) { - internal JobEntityParam_Aspect(IParameterSymbol parameterSymbol, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) - { - // Per chunk - var variableName = $"{TypeHandleFieldName}Array"; - VariableDeclarationAtStartOfExecuteMethod = $"var {variableName} = __TypeHandle.{TypeHandleFieldName}.Resolve(chunk);"; - - // Per entity - var executeMethodArgument = $"{variableName}Array"; - ExecuteMethodArgumentSetup = $"var {executeMethodArgument} = {variableName}[entityIndexInChunk];"; - ExecuteMethodArgumentValue = executeMethodArgument; - } + // Per chunk + var variableName = $"{TypeHandleFieldName}Array"; + VariableDeclarationAtStartOfExecuteMethod = $"var {variableName} = __TypeHandle.{TypeHandleFieldName}.Resolve(chunk);"; + + // Per entity + var executeMethodArgument = $"{variableName}Array"; + ExecuteMethodArgumentSetup = $"var {executeMethodArgument} = {variableName}[entityIndexInChunk];"; + ExecuteMethodArgumentValue = executeMethodArgument; } +} - public class JobEntityParam_ManagedComponent : JobEntityParam +public class JobEntityParam_ManagedComponent : JobEntityParam +{ + internal JobEntityParam_ManagedComponent(IParameterSymbol parameterSymbol, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) { - internal JobEntityParam_ManagedComponent(IParameterSymbol parameterSymbol, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) - { - var accessorVariableName = $"{parameterSymbol.Name}ManagedComponentAccessor"; - VariableDeclarationAtStartOfExecuteMethod = $"var {accessorVariableName} = chunk.GetManagedComponentAccessor(ref __TypeHandle.{TypeHandleFieldName}, __EntityManager);"; + var accessorVariableName = $"{parameterSymbol.Name}ManagedComponentAccessor"; + VariableDeclarationAtStartOfExecuteMethod = $"var {accessorVariableName} = chunk.GetManagedComponentAccessor(ref __TypeHandle.{TypeHandleFieldName}, __EntityManager);"; - var localName = ExecuteMethodArgumentValue = $"retrievedByIndexIn{accessorVariableName}"; - ExecuteMethodArgumentSetup = $"var {localName} = {accessorVariableName}[entityIndexInChunk];"; - } + var localName = ExecuteMethodArgumentValue = $"retrievedByIndexIn{accessorVariableName}"; + ExecuteMethodArgumentSetup = $"var {localName} = {accessorVariableName}[entityIndexInChunk];"; } +} - public class JobEntityParam_ComponentData : JobEntityParam +public class JobEntityParam_ComponentData : JobEntityParam +{ + internal JobEntityParam_ComponentData( + IParameterSymbol parameterSymbol, + ITypeSymbol componentTypeSymbol, + bool isReadOnly, + ComponentRefWrapperType componentRefWrapperType, + bool performSafetyChecks, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) { - internal JobEntityParam_ComponentData( - IParameterSymbol parameterSymbol, - ITypeSymbol componentTypeSymbol, - bool isReadOnly, - RefWrapperType refWrapperType, - bool performSafetyChecks, string typeHandleFieldName) : base(parameterSymbol, typeHandleFieldName) - { - TypeSymbol = componentTypeSymbol; - IsReadOnly = isReadOnly; - RefWrapperType = refWrapperType; - IsZeroSizedComponent = componentTypeSymbol.IsZeroSizedComponent(); - IsEnableableComponent = componentTypeSymbol.IsEnableableComponent(); + TypeSymbol = componentTypeSymbol; + IsReadOnly = isReadOnly; + ComponentRefWrapperType = componentRefWrapperType; + IsZeroSizedComponent = componentTypeSymbol.IsZeroSizedComponent(); + IsEnableableComponent = componentTypeSymbol.IsEnableableComponent(); - string fullyQualifiedTypeName = componentTypeSymbol.ToFullName(); + string fullyQualifiedTypeName = componentTypeSymbol.ToFullName(); - var executeMethodArg = GetIJobEntityExecuteMethodArgument(); + var executeMethodArg = GetIJobEntityExecuteMethodArgument(); - VariableDeclarationAtStartOfExecuteMethod = executeMethodArg.RequiredVariableDeclaration; - ExecuteMethodArgumentSetup = executeMethodArg.SetUp; - ExecuteMethodArgumentValue = executeMethodArg.Value; + VariableDeclarationAtStartOfExecuteMethod = executeMethodArg.RequiredVariableDeclaration; + ExecuteMethodArgumentSetup = executeMethodArg.SetUp; + ExecuteMethodArgumentValue = executeMethodArg.Value; - (string RequiredVariableDeclaration, string SetUp, string Value) GetIJobEntityExecuteMethodArgument() - { - string requiredVariableName; - string requiredVariableDeclaration; - string setUp; - string value; + (string RequiredVariableDeclaration, string SetUp, string Value) GetIJobEntityExecuteMethodArgument() + { + string requiredVariableName; + string requiredVariableDeclaration; + string setUp; + string value; - switch (refWrapperType) + switch (componentRefWrapperType) + { + case ComponentRefWrapperType.None: { - case RefWrapperType.None: + requiredVariableName = + IsZeroSizedComponent + ? string.Empty + : $"{parameterSymbol.Name}ArrayIntPtr"; + requiredVariableDeclaration = + IsZeroSizedComponent + ? string.Empty + : IsReadOnly + ? $"var {requiredVariableName} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtr<{fullyQualifiedTypeName}>(chunk, ref __TypeHandle.{TypeHandleFieldName});" + : $"var {requiredVariableName} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr<{fullyQualifiedTypeName}>(chunk, ref __TypeHandle.{TypeHandleFieldName});"; + + value = $"{requiredVariableName}Ref"; + setUp = + IsZeroSizedComponent + ? string.Empty + : $"ref var {value} = ref Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement<{fullyQualifiedTypeName}>({requiredVariableName}, entityIndexInChunk);"; + + value = ParameterSymbol.RefKind switch { - requiredVariableName = - IsZeroSizedComponent - ? string.Empty - : $"{parameterSymbol.Name}ArrayIntPtr"; - requiredVariableDeclaration = - IsZeroSizedComponent - ? string.Empty - : IsReadOnly - ? $"var {requiredVariableName} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtr<{fullyQualifiedTypeName}>(chunk, ref __TypeHandle.{TypeHandleFieldName});" - : $"var {requiredVariableName} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr<{fullyQualifiedTypeName}>(chunk, ref __TypeHandle.{TypeHandleFieldName});"; - - value = $"{requiredVariableName}Ref"; - setUp = - IsZeroSizedComponent - ? string.Empty - : $"ref var {value} = ref Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement<{fullyQualifiedTypeName}>({requiredVariableName}, entityIndexInChunk);"; - - value = ParameterSymbol.RefKind switch - { - RefKind.Ref => IsZeroSizedComponent ? "default" : $"ref {value}", - RefKind.In => IsZeroSizedComponent ? "default" : $"in {value}", - _ => IsZeroSizedComponent ? "default" : value - }; - return (requiredVariableDeclaration, setUp, value); - } - case RefWrapperType.RefRW: - { - requiredVariableName = $"{parameterSymbol.Name}ArrayIntPtr"; - requiredVariableDeclaration = $"var {requiredVariableName} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr<{fullyQualifiedTypeName}>(chunk, ref __TypeHandle.{TypeHandleFieldName});"; - - value = $"{requiredVariableName}Ref"; - setUp = - performSafetyChecks - ? $"var {value} = Unity.Entities.Internal.InternalCompilerInterface.GetRefRW<{fullyQualifiedTypeName}>({requiredVariableName}, entityIndexInChunk, ref __TypeHandle.{TypeHandleFieldName});" - : $"var {value} = Unity.Entities.Internal.InternalCompilerInterface.GetRefRW<{fullyQualifiedTypeName}>({requiredVariableName}, entityIndexInChunk);"; - return (requiredVariableDeclaration, setUp, value); - } - case RefWrapperType.RefRO: - { - requiredVariableName = $"{parameterSymbol.Name}ArrayIntPtr"; - requiredVariableDeclaration = $"var {requiredVariableName} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtr<{fullyQualifiedTypeName}>(chunk, ref __TypeHandle.{TypeHandleFieldName});"; - - value = $"{requiredVariableName}Ref"; - setUp = - performSafetyChecks - ? $"var {value} = Unity.Entities.Internal.InternalCompilerInterface.GetRefRO<{fullyQualifiedTypeName}>({requiredVariableName}, entityIndexInChunk, ref __TypeHandle.{TypeHandleFieldName});" - : $"var {value} = Unity.Entities.Internal.InternalCompilerInterface.GetRefRO<{fullyQualifiedTypeName}>({requiredVariableName}, entityIndexInChunk);"; - return (requiredVariableDeclaration, setUp, value); - } - case RefWrapperType.EnabledRefRO: - case RefWrapperType.EnabledRefRW: - { - requiredVariableName = $"{parameterSymbol.Name}EnabledMask_{(IsReadOnly ? "RO" : "RW")}"; - requiredVariableDeclaration = $"var {requiredVariableName} = chunk.GetEnabledMask(ref __TypeHandle.{TypeHandleFieldName});"; + RefKind.Ref => IsZeroSizedComponent ? "default" : $"ref {value}", + RefKind.In => IsZeroSizedComponent ? "default" : $"in {value}", + _ => IsZeroSizedComponent ? "default" : value + }; + return (requiredVariableDeclaration, setUp, value); + } + case ComponentRefWrapperType.RefRW: + { + requiredVariableName = $"{parameterSymbol.Name}ArrayIntPtr"; + requiredVariableDeclaration = $"var {requiredVariableName} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr<{fullyQualifiedTypeName}>(chunk, ref __TypeHandle.{TypeHandleFieldName});"; + + value = $"{requiredVariableName}Ref"; + setUp = + performSafetyChecks + ? $"var {value} = Unity.Entities.Internal.InternalCompilerInterface.GetRefRW<{fullyQualifiedTypeName}>({requiredVariableName}, entityIndexInChunk, ref __TypeHandle.{TypeHandleFieldName});" + : $"var {value} = Unity.Entities.Internal.InternalCompilerInterface.GetRefRW<{fullyQualifiedTypeName}>({requiredVariableName}, entityIndexInChunk);"; + return (requiredVariableDeclaration, setUp, value); + } + case ComponentRefWrapperType.RefRO: + { + requiredVariableName = $"{parameterSymbol.Name}ArrayIntPtr"; + requiredVariableDeclaration = $"var {requiredVariableName} = Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtr<{fullyQualifiedTypeName}>(chunk, ref __TypeHandle.{TypeHandleFieldName});"; + + value = $"{requiredVariableName}Ref"; + setUp = + performSafetyChecks + ? $"var {value} = Unity.Entities.Internal.InternalCompilerInterface.GetRefRO<{fullyQualifiedTypeName}>({requiredVariableName}, entityIndexInChunk, ref __TypeHandle.{TypeHandleFieldName});" + : $"var {value} = Unity.Entities.Internal.InternalCompilerInterface.GetRefRO<{fullyQualifiedTypeName}>({requiredVariableName}, entityIndexInChunk);"; + return (requiredVariableDeclaration, setUp, value); + } + case ComponentRefWrapperType.EnabledRefRO: + case ComponentRefWrapperType.EnabledRefRW: + { + requiredVariableName = $"{parameterSymbol.Name}EnabledMask_{(IsReadOnly ? "RO" : "RW")}"; + requiredVariableDeclaration = $"var {requiredVariableName} = chunk.GetEnabledMask(ref __TypeHandle.{TypeHandleFieldName});"; - value = $"{requiredVariableName}.{(IsReadOnly ? "GetEnabledRefRO" : "GetEnabledRefRW")}<{fullyQualifiedTypeName}>(entityIndexInChunk)"; - return (requiredVariableDeclaration, SetUp: default, value); - } - default: - throw new ArgumentOutOfRangeException(); + value = $"{requiredVariableName}.{(IsReadOnly ? "GetEnabledRefRO" : "GetEnabledRefRW")}<{fullyQualifiedTypeName}>(entityIndexInChunk)"; + return (requiredVariableDeclaration, SetUp: default, value); } + default: + throw new ArgumentOutOfRangeException(); } } - public RefWrapperType RefWrapperType { get; } - public bool IsZeroSizedComponent { get; } - public bool IsEnableableComponent { get; } } - public interface IAttributeParameter + public ComponentRefWrapperType ComponentRefWrapperType { get; } + public bool IsZeroSizedComponent { get; } + public bool IsEnableableComponent { get; } +} +public interface IAttributeParameter +{ + public bool IsInt { get; } + public string AttributeName { get; } +} + +class JobEntityParamValueTypesPassedWithDefaultArguments : JobEntityParam +{ + internal JobEntityParamValueTypesPassedWithDefaultArguments(IParameterSymbol parameterSymbol) : base(parameterSymbol, "") { - public bool IsInt { get; } - public string AttributeName { get; } + ExecuteMethodArgumentValue = "default"; } +} - class JobEntityParamValueTypesPassedWithDefaultArguments : JobEntityParam +class JobEntityParam_EntityIndexInQuery : JobEntityParam, IAttributeParameter +{ + public bool IsInt => TypeSymbol.IsInt(); + public string AttributeName => "EntityIndexInQuery"; + + internal JobEntityParam_EntityIndexInQuery(IParameterSymbol parameterSymbol) : base(parameterSymbol, string.Empty) { - internal JobEntityParamValueTypesPassedWithDefaultArguments(IParameterSymbol parameterSymbol) : base(parameterSymbol, "") - { - ExecuteMethodArgumentValue = "default"; - } + ExecuteMethodArgumentSetup = " var entityIndexInQuery = __ChunkBaseEntityIndices[chunkIndexInQuery] + matchingEntityCount;"; + ExecuteMethodArgumentValue = "entityIndexInQuery"; } +} - class JobEntityParam_EntityIndexInQuery : JobEntityParam, IAttributeParameter +class JobEntityParam_ChunkIndexInQuery : JobEntityParam, IAttributeParameter +{ + public bool IsInt => TypeSymbol.IsInt(); + public string AttributeName => "ChunkIndexInQuery"; + internal JobEntityParam_ChunkIndexInQuery(IParameterSymbol parameterSymbol) : base(parameterSymbol, string.Empty) { - public bool IsInt => TypeSymbol.IsInt(); - public string AttributeName => "EntityIndexInQuery"; - - internal JobEntityParam_EntityIndexInQuery(IParameterSymbol parameterSymbol) : base(parameterSymbol, string.Empty) - { - ExecuteMethodArgumentSetup = " var entityIndexInQuery = __ChunkBaseEntityIndices[chunkIndexInQuery] + matchingEntityCount;"; - ExecuteMethodArgumentValue = "entityIndexInQuery"; - } + // TODO(DOTS-6130): an extra helper job is needed to provided the chunk index in query when the query has chunk filtering enabled. + // For now this is an unfiltered chunk index. + ExecuteMethodArgumentValue = "chunkIndexInQuery"; } +} - class JobEntityParam_ChunkIndexInQuery : JobEntityParam, IAttributeParameter +class JobEntityParam_EntityIndexInChunk : JobEntityParam, IAttributeParameter +{ + public bool IsInt => TypeSymbol.IsInt(); + public string AttributeName => "EntityIndexInChunk"; + internal JobEntityParam_EntityIndexInChunk(IParameterSymbol parameterSymbol) : base(parameterSymbol, string.Empty) { - public bool IsInt => TypeSymbol.IsInt(); - public string AttributeName => "ChunkIndexInQuery"; - internal JobEntityParam_ChunkIndexInQuery(IParameterSymbol parameterSymbol) : base(parameterSymbol, string.Empty) - { - // TODO(DOTS-6130): an extra helper job is needed to provided the chunk index in query when the query has chunk filtering enabled. - // For now this is an unfiltered chunk index. - ExecuteMethodArgumentValue = "chunkIndexInQuery"; - } + ExecuteMethodArgumentValue = "entityIndexInChunk"; } +} - class JobEntityParam_EntityIndexInChunk : JobEntityParam, IAttributeParameter +public abstract class JobEntityParam +{ + public bool RequiresExecuteMethodArgumentSetup => !string.IsNullOrEmpty(ExecuteMethodArgumentSetup); + public string ExecuteMethodArgumentSetup { get; protected set; } + public string ExecuteMethodArgumentValue { get; protected set; } + public IParameterSymbol ParameterSymbol { get; } + public ITypeSymbol TypeSymbol { get; protected set; } + + public string TypeHandleFieldName { get; } + + public bool IsReadOnly { get; protected set; } + public string VariableDeclarationAtStartOfExecuteMethod { get; protected set; } + + internal static (bool Success, JobEntityParam JobEntityParameter) TryParseComponentTypeSymbol( + ITypeSymbol componentTypeSymbol, + IParameterSymbol parameterSymbol, + bool isReadOnly, + bool performSafetyChecks, + ISourceGeneratorDiagnosable diagnosable, + string typeHandleFieldName, + string constructedFrom = null) { - public bool IsInt => TypeSymbol.IsInt(); - public string AttributeName => "EntityIndexInChunk"; - internal JobEntityParam_EntityIndexInChunk(IParameterSymbol parameterSymbol) : base(parameterSymbol, string.Empty) + var refWrapperType = constructedFrom switch + { + "global::Unity.Entities.RefRW" => ComponentRefWrapperType.RefRW, + "global::Unity.Entities.RefRO" => ComponentRefWrapperType.RefRO, + "global::Unity.Entities.EnabledRefRW" => ComponentRefWrapperType.EnabledRefRW, + "global::Unity.Entities.EnabledRefRO" => ComponentRefWrapperType.EnabledRefRO, + _ => ComponentRefWrapperType.None + }; + + if (componentTypeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.Arity != 0) { - ExecuteMethodArgumentValue = "entityIndexInChunk"; + JobEntityGeneratorErrors.SGJE0011(diagnosable, parameterSymbol.Locations.Single(), parameterSymbol.Name); + return (false, default); } + return (true, new JobEntityParam_ComponentData(parameterSymbol, componentTypeSymbol, isReadOnly, refWrapperType, performSafetyChecks, typeHandleFieldName)); } - public abstract class JobEntityParam + internal JobEntityParam(IParameterSymbol parameterSymbol, string typeHandleFieldName) { - public bool RequiresExecuteMethodArgumentSetup => !string.IsNullOrEmpty(ExecuteMethodArgumentSetup); - public string ExecuteMethodArgumentSetup { get; protected set; } - public string ExecuteMethodArgumentValue { get; protected set; } - public IParameterSymbol ParameterSymbol { get; } - public ITypeSymbol TypeSymbol { get; protected set; } - - public string TypeHandleFieldName { get; } - - public bool IsReadOnly { get; protected set; } - public string VariableDeclarationAtStartOfExecuteMethod { get; protected set; } - - internal static (bool Success, JobEntityParam JobEntityParameter) TryParseComponentTypeSymbol( - ITypeSymbol componentTypeSymbol, - IParameterSymbol parameterSymbol, - bool isReadOnly, - bool performSafetyChecks, - ISourceGeneratorDiagnosable diagnosable, - string typeHandleFieldName, - string constructedFrom = null) - { - var refWrapperType = constructedFrom switch - { - "global::Unity.Entities.RefRW" => RefWrapperType.RefRW, - "global::Unity.Entities.RefRO" => RefWrapperType.RefRO, - "global::Unity.Entities.EnabledRefRW" => RefWrapperType.EnabledRefRW, - "global::Unity.Entities.EnabledRefRO" => RefWrapperType.EnabledRefRO, - _ => RefWrapperType.None - }; - - if (componentTypeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.Arity != 0) - { - JobEntityGeneratorErrors.SGJE0011(diagnosable, parameterSymbol.Locations.Single(), parameterSymbol.Name); - return (false, default); - } - return (true, new JobEntityParam_ComponentData(parameterSymbol, componentTypeSymbol, isReadOnly, refWrapperType, performSafetyChecks, typeHandleFieldName)); - } - - internal JobEntityParam(IParameterSymbol parameterSymbol, string typeHandleFieldName) - { - ParameterSymbol = parameterSymbol; - TypeSymbol = parameterSymbol.Type; - TypeHandleFieldName = typeHandleFieldName; - IsReadOnly = parameterSymbol.IsReadOnly(); - } + ParameterSymbol = parameterSymbol; + TypeSymbol = parameterSymbol.Type; + TypeHandleFieldName = typeHandleFieldName; + IsReadOnly = parameterSymbol.IsReadOnly(); } } diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescriptionHelperMethods.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescriptionHelperMethods.cs index ae701f2..9f9a4af 100644 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescriptionHelperMethods.cs +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescriptionHelperMethods.cs @@ -1,54 +1,53 @@ using System; using Microsoft.CodeAnalysis; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +public partial class JobEntityDescription { - public partial class JobEntityDescription + private static bool IsLessAccessibleThan(ISymbol executeParameterSymbol, ISymbol jobEntityTypeSymbol) { - private static bool IsLessAccessibleThan(ITypeSymbol executeParameterSymbol, ITypeSymbol jobEntityTypeSymbol) - { - var jobEntityAccessibility = jobEntityTypeSymbol.DeclaredAccessibility; - var jobEntityContainingType = jobEntityTypeSymbol.ContainingType; - - while (jobEntityContainingType != null) - { - // E.g. if an `IJobEntity` type is declared `public` but nested within a non-public type, then its accessibility is in fact less accessible than `public` - if (IsLessAccessibleThan(jobEntityContainingType.DeclaredAccessibility, jobEntityAccessibility)) - jobEntityAccessibility = jobEntityContainingType.DeclaredAccessibility; + var jobEntityAccessibility = jobEntityTypeSymbol.DeclaredAccessibility; + var jobEntityContainingType = jobEntityTypeSymbol.ContainingType; - jobEntityContainingType = jobEntityContainingType.ContainingType; - } + while (jobEntityContainingType != null) + { + // E.g. if an `IJobEntity` type is declared `public` but nested within a non-public type, then its accessibility is in fact less accessible than `public` + if (IsLessAccessibleThan(jobEntityContainingType.DeclaredAccessibility, jobEntityAccessibility)) + jobEntityAccessibility = jobEntityContainingType.DeclaredAccessibility; - var executeParameterSymbolAccessibility = executeParameterSymbol.DeclaredAccessibility; - var parameterSymbolContainingType = executeParameterSymbol.ContainingType; + jobEntityContainingType = jobEntityContainingType.ContainingType; + } - while (parameterSymbolContainingType != null) - { - // E.g. if an `IComponentData` type is declared `public` but nested within a non-public type, then its accessibility is in fact less accessible than `public` - if (IsLessAccessibleThan(parameterSymbolContainingType.DeclaredAccessibility, executeParameterSymbolAccessibility)) - executeParameterSymbolAccessibility = parameterSymbolContainingType.DeclaredAccessibility; + var executeParameterSymbolAccessibility = executeParameterSymbol.DeclaredAccessibility; + var parameterSymbolContainingType = executeParameterSymbol.ContainingType; - parameterSymbolContainingType = parameterSymbolContainingType.ContainingType; - } + while (parameterSymbolContainingType != null) + { + // E.g. if an `IComponentData` type is declared `public` but nested within a non-public type, then its accessibility is in fact less accessible than `public` + if (IsLessAccessibleThan(parameterSymbolContainingType.DeclaredAccessibility, executeParameterSymbolAccessibility)) + executeParameterSymbolAccessibility = parameterSymbolContainingType.DeclaredAccessibility; - return IsLessAccessibleThan(executeParameterSymbolAccessibility, jobEntityAccessibility); + parameterSymbolContainingType = parameterSymbolContainingType.ContainingType; } - private static bool IsLessAccessibleThan(Accessibility accessibility1, Accessibility accessibility2) + return IsLessAccessibleThan(executeParameterSymbolAccessibility, jobEntityAccessibility); + } + + private static bool IsLessAccessibleThan(Accessibility accessibility1, Accessibility accessibility2) + { + return accessibility1 switch { - return accessibility1 switch - { - Accessibility.Private => accessibility2 != Accessibility.Private, - Accessibility.Internal => accessibility2 == Accessibility.ProtectedOrInternal || - accessibility2 == Accessibility.Protected || - accessibility2 == Accessibility.Public, - Accessibility.Protected => accessibility2 == Accessibility.ProtectedOrInternal || - accessibility2 == Accessibility.Internal || - accessibility2 == Accessibility.Public, - Accessibility.ProtectedOrInternal => accessibility2 == Accessibility.Public, - Accessibility.Public => false, - _ => throw new ArgumentOutOfRangeException() - }; - } + Accessibility.Private => accessibility2 != Accessibility.Private, + Accessibility.Internal => accessibility2 == Accessibility.ProtectedOrInternal || + accessibility2 == Accessibility.Protected || + accessibility2 == Accessibility.Public, + Accessibility.Protected => accessibility2 == Accessibility.ProtectedOrInternal || + accessibility2 == Accessibility.Internal || + accessibility2 == Accessibility.Public, + Accessibility.ProtectedOrInternal => accessibility2 == Accessibility.Public, + Accessibility.Public => false, + _ => throw new ArgumentOutOfRangeException() + }; } } diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescriptionSourceFactory.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescriptionSourceFactory.cs index 28a180e..7d35e77 100644 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescriptionSourceFactory.cs +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityDescriptionSourceFactory.cs @@ -1,260 +1,197 @@ -using System; -using System.Collections.Generic; +using System.CodeDom.Compiler; +using System.IO; using System.Linq; -using System.Text; using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +public partial class JobEntityDescription { - public partial class JobEntityDescription + public string Generate() { - public string Generate() + var hasEnableableComponent = true; // HasEnableableComponent(); + // Currently, it is never set to false. + // It could be false in cases where the IJobEntity is constructing its own EntityQuery based on the provided parameters, + // in which case it can statically determine at source-generation time whether the query contains any enableable components + // (and thus whether it needs to generate the extra code to handle enabled bits correctly). + // This work has not yet been implemented. (e.g. when enableablebits is turned of with static query generation.) + // Check should be based on whether the query contains enableablebits, not if the parameters does (cause some chunks might still need to check if they are enabled) + // Discussion: https://github.cds.internal.unity3d.com/unity/dots/pull/3217#discussion_r227389 + // Also do this for EntitiesSourceFactory.JobStructFor if that is still a thing. + + var inheritsFromBeginEndChunk = m_JobEntityTypeSymbol.InheritsFromInterface("Unity.Entities.IJobEntityChunkBeginEnd"); + + using var stringWriter = new StringWriter(); + using var indentedTextWriter = new IndentedTextWriter(stringWriter); + + indentedTextWriter.WriteLine("[global::System.Runtime.CompilerServices.CompilerGenerated]"); + indentedTextWriter.WriteLine($"partial struct {TypeName} : global::Unity.Entities.IJobChunk"); + indentedTextWriter.WriteLine("{"); + indentedTextWriter.Indent++; + + indentedTextWriter.WriteLine("InternalCompilerQueryAndHandleData.TypeHandle __TypeHandle;"); + if (m_RequiresEntityManager) + indentedTextWriter.WriteLine("public global::Unity.Entities.EntityManager __EntityManager;"); + if (m_HasEntityIndexInQuery) + indentedTextWriter.WriteLine("[global::Unity.Collections.ReadOnly] public global::Unity.Collections.NativeArray __ChunkBaseEntityIndices;"); + + indentedTextWriter.WriteExecuteMethod(inheritsFromBeginEndChunk, hasEnableableComponent, _userExecuteMethodParams); + + if (inheritsFromBeginEndChunk) { - var hasEnableableComponent = true; // HasEnableableComponent(); - // Currently, it is never set to false. - // It could be false in cases where the IJobEntity is constructing its own EntityQuery based on the provided parameters, - // in which case it can statically determine at source-generation time whether the query contains any enableable components - // (and thus whether it needs to generate the extra code to handle enabled bits correctly). - // This work has not yet been implemented. (e.g. when enableablebits is turned of with static query generation.) - // Check should be based on whether the query contains enableablebits, not if the parameters does (cause some chunks might still need to check if they are enabled) - // Discussion: https://github.cds.internal.unity3d.com/unity/dots/pull/3217#discussion_r227389 - // Also do this for EntitiesSourceFactory.JobStructFor if that is still a thing. - - var inheritsFromBeginEndChunk = m_JobEntityTypeSymbol.InheritsFromInterface("Unity.Entities.IJobEntityChunkBeginEnd"); - string partialStructImplementation = - $@"[global::System.Runtime.CompilerServices.CompilerGenerated] -partial struct {TypeName} : global::Unity.Entities.IJobChunk -{{ - InternalCompilerQueryAndHandleData.TypeHandle __TypeHandle; - {EntityManager()} - {ChunkBaseEntityIndices()} - - [global::System.Runtime.CompilerServices.CompilerGenerated] - public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkIndexInQuery, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask) - {{ - {$"var shouldExecuteChunk = OnChunkBegin(in chunk, chunkIndexInQuery, useEnabledMask, in chunkEnabledMask);{Environment.NewLine} if (shouldExecuteChunk){{".EmitIfTrue(inheritsFromBeginEndChunk)} - {string.Join(Environment.NewLine + " ", UserExecuteMethodParams.Select(p => p.VariableDeclarationAtStartOfExecuteMethod).Distinct())} - int chunkEntityCount = chunk.Count; - int matchingEntityCount = 0; - {@"if (!useEnabledMask) - {".EmitIfTrue(hasEnableableComponent)} - for(int entityIndexInChunk = 0; entityIndexInChunk < chunkEntityCount; ++entityIndexInChunk) - {{ - {string.Join(Environment.NewLine + " ", UserExecuteMethodParams.Where(p => p.RequiresExecuteMethodArgumentSetup).Select(p => p.ExecuteMethodArgumentSetup))} - Execute({UserExecuteMethodParams.Select(param => param.ExecuteMethodArgumentValue).SeparateByComma()}); - matchingEntityCount++; - }} - {$@"}} - else - {{ - int edgeCount = global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong0 ^ (chunkEnabledMask.ULong0 << 1)) + - global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong1 ^ (chunkEnabledMask.ULong1 << 1)) - 1; - bool useRanges = edgeCount <= 4; - if (useRanges) - {{ - int entityIndexInChunk = 0; - int chunkEndIndex = 0; - while (global::Unity.Entities.EnabledBitUtility.TryGetNextRange(chunkEnabledMask, chunkEndIndex, out entityIndexInChunk, out chunkEndIndex)) - {{ - while (entityIndexInChunk < chunkEndIndex) - {{ - {string.Join(Environment.NewLine + " ", UserExecuteMethodParams.Where(p => p.RequiresExecuteMethodArgumentSetup).Select(p => p.ExecuteMethodArgumentSetup))} - Execute({UserExecuteMethodParams.Select(param => param.ExecuteMethodArgumentValue).SeparateByComma()}); - entityIndexInChunk++; - matchingEntityCount++; - }} - }} - }} - else - {{ - ulong mask64 = chunkEnabledMask.ULong0; - int count = global::Unity.Mathematics.math.min(64, chunkEntityCount); - for (int entityIndexInChunk = 0; entityIndexInChunk < count; ++entityIndexInChunk) - {{ - if ((mask64 & 1) != 0) - {{ - {string.Join(Environment.NewLine + " ", UserExecuteMethodParams.Where(p => p.RequiresExecuteMethodArgumentSetup).Select(p => p.ExecuteMethodArgumentSetup))} - Execute({UserExecuteMethodParams.Select(param => param.ExecuteMethodArgumentValue).SeparateByComma()}); - matchingEntityCount++; - }} - mask64 >>= 1; - }} - mask64 = chunkEnabledMask.ULong1; - for (int entityIndexInChunk = 64; entityIndexInChunk < chunkEntityCount; ++entityIndexInChunk) - {{ - if ((mask64 & 1) != 0) - {{ - {string.Join(Environment.NewLine + " ", UserExecuteMethodParams.Where(p => p.RequiresExecuteMethodArgumentSetup).Select(p => p.ExecuteMethodArgumentSetup))} - Execute({UserExecuteMethodParams.Select(param => param.ExecuteMethodArgumentValue).SeparateByComma()}); - matchingEntityCount++; - }} - mask64 >>= 1; - }} - }} - }}".EmitIfTrue(hasEnableableComponent)} - {"} OnChunkEnd(in chunk, chunkIndexInQuery, useEnabledMask, in chunkEnabledMask, shouldExecuteChunk);".EmitIfTrue(inheritsFromBeginEndChunk)} - }} - {GetScheduleAndRunMethods()} - - {CreateTypeHandleSyntax(m_CheckUserDefinedQueryForScheduling)} - - /// Internal structure used by the compiler - {MakeInternalCompilerStructure()} -}}"; - - return partialStructImplementation.ReplaceLineEndings(); + indentedTextWriter.WriteLine("OnChunkEnd(in chunk, chunkIndexInQuery, useEnabledMask, in chunkEnabledMask, shouldExecuteChunk);"); + indentedTextWriter.Indent--; + indentedTextWriter.WriteLine("}"); } - string CreateTypeHandleSyntax(bool checkQuery = false) - { - const string jobExtensions = "global::Unity.Entities.JobChunkExtensions"; - const string internalJobExtensions = "global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface"; - - var additionalSyntax = - @$"{MethodsForGettingRequiredComponents(checkQuery)} + indentedTextWriter.WriteScheduleAndRunMethods(); + QueriesAndHandles.WriteInternalCompilerQueryAndHandleDataStruct( + indentedTextWriter, + shouldHaveUpdate: true, + fieldsArePublic: true, + writeAdditionalSyntaxInInternalCompilerQueryAndHandleData: WriteAdditionalSyntax, + _queriesAndHandles); - public void Init(ref global::Unity.Entities.SystemState state, bool assignDefaultQuery) - {{ - if (assignDefaultQuery) {{ - __AssignQueries(ref state); - }} - __TypeHandle.__AssignHandles(ref state); - }} + indentedTextWriter.WriteInternalCompilerStruct(m_RequiresEntityManager); - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void Run(ref {FullTypeName} job, global::Unity.Entities.EntityQuery query) - {{ - job.__TypeHandle = __TypeHandle; - {(HasManagedComponents ? internalJobExtensions : jobExtensions)}.{(HasManagedComponents ? "RunByRefWithoutJobs(ref job, query)" : "RunByRef(ref job, query)")}; - }} - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public global::Unity.Jobs.JobHandle Schedule(ref {FullTypeName} job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency) - {{ - job.__TypeHandle = __TypeHandle; - return {jobExtensions}.ScheduleByRef(ref job, query, dependency); - }} - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public global::Unity.Jobs.JobHandle ScheduleParallel(ref {FullTypeName} job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency) - {{ - job.__TypeHandle = __TypeHandle; - return {(m_HasEntityIndexInQuery ? internalJobExtensions : jobExtensions)}.ScheduleParallelByRef(ref job, query, dependency{(m_HasEntityIndexInQuery?", job.__ChunkBaseEntityIndices":"")}); - }} - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void UpdateBaseEntityIndexArray(ref {FullTypeName} job, global::Unity.Entities.EntityQuery query, ref global::Unity.Entities.SystemState state) - {{ - {(m_HasEntityIndexInQuery ? @"var baseEntityIndexArray = query.CalculateBaseEntityIndexArray(state.WorldUpdateAllocator); - job.__ChunkBaseEntityIndices = baseEntityIndexArray;" : "")} - }} - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public global::Unity.Jobs.JobHandle UpdateBaseEntityIndexArray(ref {FullTypeName} job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency, ref global::Unity.Entities.SystemState state) - {{ - {(m_HasEntityIndexInQuery ? @"var baseEntityIndexArray = query.CalculateBaseEntityIndexArrayAsync(state.WorldUpdateAllocator, dependency, out var indexDependency); - job.__ChunkBaseEntityIndices = baseEntityIndexArray; - return indexDependency;" : "return dependency;")} - }} + indentedTextWriter.Indent--; + indentedTextWriter.WriteLine("}"); + return stringWriter.ToString(); + } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void AssignEntityManager(ref {FullTypeName} job, global::Unity.Entities.EntityManager entityManager) - {{ - {(m_RequiresEntityManager ? @"job.__EntityManager = entityManager;" : "")} - }} - "; + void WriteAdditionalSyntax(IndentedTextWriter writer) + { + const string jobExtensions = "global::Unity.Entities.JobChunkExtensions"; + const string internalJobExtensions = "global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface"; - return HandlesDescription.GetTypeHandleForInitialPartial(true, true, additionalSyntax, m_HandlesDescription); + // Generate all methods required for throwing a runtime exception if users schedule/run jobs with incompatible queries. + if (m_CheckUserDefinedQueryForScheduling) + { + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine($"public static int GetRequiredComponentTypeCount() => {m_ComponentTypesInExecuteMethod.Count}{m_AspectTypesInExecuteMethod.Select(a => $" + {a.TypeSymbol.ToFullName()}.GetRequiredComponentTypeCount()").SeparateBy("")};"); + + writer.WriteLine(); + writer.WriteLine("public static void AddRequiredComponentTypes(ref global::System.Span components)"); + writer.WriteLine("{"); + writer.Indent++; + for (var index = 0; index < m_ComponentTypesInExecuteMethod.Count; index++) + writer.WriteLine($"components[{index}] = {m_ComponentTypesInExecuteMethod[index].ToString()};"); + if (m_AspectTypesInExecuteMethod.Count > 0) + { + writer.WriteLine($"int startAddIndex = {m_ComponentTypesInExecuteMethod.Count};"); + + for (var index = 0; index < m_AspectTypesInExecuteMethod.Count; index++) + { + var aspect = m_AspectTypesInExecuteMethod[index]; + var aspectFullName = aspect.TypeSymbol.ToFullName(); + + writer.WriteLine($"int aspect{index}ComponentTypeCount = {aspectFullName}.GetRequiredComponentTypeCount();"); + writer.WriteLine($"global::System.Span aspect{index}Components = stackalloc global::Unity.Entities.ComponentType[aspect{index}ComponentTypeCount];"); + writer.WriteLine($"{aspectFullName}.AddRequiredComponentTypes(ref aspect{index}Components);"); + writer.WriteLine($@"for (int i = 0; i < aspect{index}ComponentTypeCount; i++)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"components[startAddIndex + i] = aspect{index}Components[i];"); + writer.Indent--; + writer.WriteLine("}"); + + if (index < m_AspectTypesInExecuteMethod.Count - 1) + writer.WriteLine( $"startAddIndex += aspect{index}ComponentTypeCount;"); + } + } + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine(); + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine("public static bool QueryHasRequiredComponentsForExecuteMethodToRun(ref EntityQuery userDefinedQuery, ref global::System.Span components) =>"); + writer.Indent++; + writer.WriteLine("global::Unity.Entities.Internal.InternalCompilerInterface.EntityQueryInterface.HasComponentsRequiredForExecuteMethodToRun(ref userDefinedQuery, ref components);"); + writer.Indent--; } - private string MethodsForGettingRequiredComponents(bool checkQuery) + writer.WriteLine(); + writer.WriteLine("public void Init(ref global::Unity.Entities.SystemState state, bool assignDefaultQuery)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("if (assignDefaultQuery)"); + writer.Indent++; + writer.WriteLine("__AssignQueries(ref state);"); + writer.Indent--; + writer.WriteLine("__TypeHandle.__AssignHandles(ref state);"); + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine(); + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine($"public void Run(ref {FullTypeName} job, global::Unity.Entities.EntityQuery query)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("job.__TypeHandle = __TypeHandle;"); + writer.WriteLine($"{(m_RequiresEntityManager ? internalJobExtensions : jobExtensions)}.{(m_RequiresEntityManager ? "RunByRefWithoutJobs(ref job, query)" : "RunByRef(ref job, query)")};"); + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine(); + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine($"public global::Unity.Jobs.JobHandle Schedule(ref {FullTypeName} job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("job.__TypeHandle = __TypeHandle;"); + writer.WriteLine("return global::Unity.Entities.JobChunkExtensions.ScheduleByRef(ref job, query, dependency);"); + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine(); + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine($"public global::Unity.Jobs.JobHandle ScheduleParallel(ref {FullTypeName} job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("job.__TypeHandle = __TypeHandle;"); + writer.WriteLine($"return {(m_HasEntityIndexInQuery ? internalJobExtensions : jobExtensions)}.ScheduleParallelByRef(ref job, query, dependency{(m_HasEntityIndexInQuery?", job.__ChunkBaseEntityIndices" : string.Empty)});"); + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine(); + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine($"public void UpdateBaseEntityIndexArray(ref {FullTypeName} job, global::Unity.Entities.EntityQuery query, ref global::Unity.Entities.SystemState state)"); + writer.WriteLine("{"); + if (m_HasEntityIndexInQuery) { - if (!checkQuery) - return string.Empty; - - return - $@"[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static int GetRequiredComponentTypeCount() - => {m_ComponentTypesInExecuteMethod.Count}{m_AspectTypesInExecuteMethod.Select(a => $" + {a.TypeSymbol.ToFullName()}.GetRequiredComponentTypeCount()").SeparateBy("")}; - - public static void AddRequiredComponentTypes(ref global::System.Span components) - {{ - {m_ComponentTypesInExecuteMethod.Select((c, i) => $"components[{i}] = {c.ToString()};").SeparateByNewLine()} - {AddAspectComponents(m_AspectTypesInExecuteMethod).SeparateByNewLine()} - }} - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static bool QueryHasRequiredComponentsForExecuteMethodToRun(ref EntityQuery userDefinedQuery, ref global::System.Span components) - => global::Unity.Entities.Internal.InternalCompilerInterface.EntityQueryInterface.HasComponentsRequiredForExecuteMethodToRun(ref userDefinedQuery, ref components);"; + writer.Indent++; + writer.WriteLine( "var baseEntityIndexArray = query.CalculateBaseEntityIndexArray(state.WorldUpdateAllocator);"); + writer.WriteLine( "job.__ChunkBaseEntityIndices = baseEntityIndexArray;"); + writer.Indent--; } - - private IEnumerable AddAspectComponents(List aspectsUsedInJobEntity) + writer.WriteLine("}"); + + writer.WriteLine(); + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine($"public global::Unity.Jobs.JobHandle UpdateBaseEntityIndexArray(ref {FullTypeName} job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency, ref global::Unity.Entities.SystemState state)"); + writer.WriteLine("{"); + writer.Indent++; + if (m_HasEntityIndexInQuery) { - if (aspectsUsedInJobEntity.Count == 0) - yield break; - - yield return $"int startAddIndex = {m_ComponentTypesInExecuteMethod.Count};"; - for (var index = 0; index < aspectsUsedInJobEntity.Count; index++) - { - var aspect = aspectsUsedInJobEntity[index]; - - yield return $"int aspect{index}ComponentTypeCount = {aspect.TypeSymbol.ToFullName()}.GetRequiredComponentTypeCount();"; - yield return $"global::System.Span aspect{index}Components = stackalloc global::Unity.Entities.ComponentType[aspect{index}ComponentTypeCount];"; - yield return $"{aspect.TypeSymbol.ToFullName()}.AddRequiredComponentTypes(ref aspect{index}Components);"; - yield return $@"for (int i = 0; i < aspect{index}ComponentTypeCount; i++) - {{ - components[startAddIndex + i] = aspect{index}Components[i]; - }}"; - if (index < aspectsUsedInJobEntity.Count - 1) - yield return $"startAddIndex += aspect{index}ComponentTypeCount;"; - } + writer.WriteLine( "var baseEntityIndexArray = query.CalculateBaseEntityIndexArrayAsync(state.WorldUpdateAllocator, dependency, out var indexDependency);"); + writer.WriteLine( "job.__ChunkBaseEntityIndices = baseEntityIndexArray;"); + writer.WriteLine( "return indexDependency;"); } - - string MakeInternalCompilerStructure() => @$"public struct InternalCompiler - {{ - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - [global::System.Diagnostics.Conditional(""ENABLE_UNITY_COLLECTIONS_CHECKS"")] - // scheduleType 0:Run, 1:Schedule, 2:ScheduleParallel - public static void CheckForErrors(int scheduleType) {{ - {@"if(scheduleType == 2) throw new global::System.InvalidOperationException(""Tried to ScheduleParallel a job with a managed execute signature. Please use .Run or .Schedule instead."");".EmitIfTrue(m_RequiresEntityManager)} - }} - }}"; - - string EntityManager() => "public global::Unity.Entities.EntityManager __EntityManager;".EmitIfTrue(m_RequiresEntityManager); - - string ChunkBaseEntityIndices() => "[global::Unity.Collections.ReadOnly] public global::Unity.Collections.NativeArray __ChunkBaseEntityIndices;".EmitIfTrue(m_HasEntityIndexInQuery); - - string GetScheduleAndRunMethods() + else + writer.WriteLine("return dependency;"); + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine(); + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine($"public void AssignEntityManager(ref {FullTypeName} job, global::Unity.Entities.EntityManager entityManager)"); + writer.WriteLine("{"); + if (m_RequiresEntityManager) { - const string source = @"global::Unity.Jobs.JobHandle __ThrowCodeGenException() => throw new global::System.Exception(""This method should have been replaced by source gen.""); - - // Emitted to disambiguate scheduling method invocations - public void Run() => __ThrowCodeGenException(); - public void RunByRef() => __ThrowCodeGenException(); - public void Run(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - public void RunByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - - public global::Unity.Jobs.JobHandle Schedule(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle ScheduleByRef(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle Schedule(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle ScheduleByRef(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); - public void Schedule() => __ThrowCodeGenException(); - public void ScheduleByRef() => __ThrowCodeGenException(); - public void Schedule(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - public void ScheduleByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - - public global::Unity.Jobs.JobHandle ScheduleParallel(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle ScheduleParallelByRef(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle ScheduleParallel(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle ScheduleParallelByRef(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle ScheduleParallel(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn, global::Unity.Collections.NativeArray chunkBaseEntityIndices) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle ScheduleParallelByRef(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn, global::Unity.Collections.NativeArray chunkBaseEntityIndices) => __ThrowCodeGenException(); - public void ScheduleParallel() => __ThrowCodeGenException(); - public void ScheduleParallelByRef() => __ThrowCodeGenException(); - public void ScheduleParallel(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - public void ScheduleParallelByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException();"; - - return source; + writer.Indent++; + writer.WriteLine("job.__EntityManager = entityManager;"); + writer.Indent--; } + writer.WriteLine("}"); } } diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityGenerator.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityGenerator.cs index ab050d1..5efc673 100644 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityGenerator.cs +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityGenerator.cs @@ -3,7 +3,6 @@ using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; -using Unity.Entities.SourceGen.SystemGenerator.Common; /* This is the JobEntityGenerator for the IJobEntity feature. @@ -11,86 +10,84 @@ to make this feature work. */ -namespace Unity.Entities.SourceGen.JobEntity +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +[Generator] +public class JobEntityGenerator : IIncrementalGenerator { - [Generator] - public class JobEntityGenerator : IIncrementalGenerator + public static readonly string GeneratorName = "JobEntity"; + + public void Initialize(IncrementalGeneratorInitializationContext context) { - public static readonly string GeneratorName = "JobEntity"; + var projectPathProvider = IncrementalSourceGenHelpers.GetSourceGenConfigProvider(context); - public void Initialize(IncrementalGeneratorInitializationContext context) - { - var projectPathProvider = IncrementalSourceGenHelpers.GetSourceGenConfigProvider(context); + // Do a simple filter for enums + var candidateProvider = context.SyntaxProvider + .CreateSyntaxProvider( + predicate: JobEntitySyntaxFinder.IsSyntaxTargetForGeneration, + transform: JobEntitySyntaxFinder.GetSemanticTargetForGeneration) + .Where(t => t is {}); - // Do a simple filter for enums - var candidateProvider = context.SyntaxProvider - .CreateSyntaxProvider( - predicate: JobEntitySyntaxFinder.IsSyntaxTargetForGeneration, - transform: JobEntitySyntaxFinder.GetSemanticTargetForGeneration) - .Where(t => t is {}); + var compilationProvider = context.CompilationProvider; + var combined = candidateProvider.Combine(compilationProvider).Combine(projectPathProvider); - var compilationProvider = context.CompilationProvider; - var combined = candidateProvider.Combine(compilationProvider).Combine(projectPathProvider); + context.RegisterSourceOutput(combined, (productionContext, sourceProviderTuple) => + { + var ((structDeclarationSyntax, compilation), sourceGenConfig) = sourceProviderTuple; - context.RegisterSourceOutput(combined, (productionContext, sourceProviderTuple) => - { - var ((structDeclarationSyntax, compilation), sourceGenConfig) = sourceProviderTuple; + Execute(productionContext, + compilation, + structDeclarationSyntax, + checkUserDefinedQueriesForSchedulingJobs: sourceGenConfig.performSafetyChecks || sourceGenConfig.isDotsDebugMode); + }); + } - Execute(productionContext, - compilation, - structDeclarationSyntax, - checkUserDefinedQueriesForSchedulingJobs: sourceGenConfig.performSafetyChecks || sourceGenConfig.isDotsDebugMode); - }); - } + static void Execute(SourceProductionContext context, Compilation compilation, + StructDeclarationSyntax candidate, bool checkUserDefinedQueriesForSchedulingJobs) + { + if (!SourceGenHelpers.ShouldRun(compilation, context.CancellationToken)) + return; - static void Execute(SourceProductionContext context, Compilation compilation, - StructDeclarationSyntax candidate, bool checkUserDefinedQueriesForSchedulingJobs) + try { - if (!SourceGenHelpers.ShouldRun(compilation, context.CancellationToken)) - return; + context.CancellationToken.ThrowIfCancellationRequested(); + var syntaxTree = candidate.SyntaxTree; + var semanticModel = compilation.GetSemanticModel(syntaxTree); - try + var jobEntityDescription = new JobEntityDescription(candidate, semanticModel, checkUserDefinedQueriesForSchedulingJobs); + if (jobEntityDescription.Invalid) { - context.CancellationToken.ThrowIfCancellationRequested(); - var syntaxTree = candidate.SyntaxTree; - var semanticModel = compilation.GetSemanticModel(syntaxTree); - - var jobEntityDescription = new JobEntityDescription(candidate, semanticModel, checkUserDefinedQueriesForSchedulingJobs); - if (jobEntityDescription.Invalid) - { - foreach (var diagnostic in jobEntityDescription.Diagnostics) - context.ReportDiagnostic(diagnostic); - return; - } - - var generatedJobEntity = jobEntityDescription.Generate(); - var sourceFilePath = syntaxTree.GetGeneratedSourceFilePath(compilation.Assembly.Name, GeneratorName); - var outputSource = TypeCreationHelpers.GenerateSourceTextForRootNodes(sourceFilePath, candidate, - generatedJobEntity, context.CancellationToken); - - context.AddSource(syntaxTree.GetGeneratedSourceFileName(GeneratorName, candidate), outputSource); - - SourceOutputHelpers.OutputSourceToFile(sourceFilePath, () => outputSource.ToString()); + foreach (var diagnostic in jobEntityDescription.SourceGenDiagnostics) + context.ReportDiagnostic(diagnostic); + return; } - catch (Exception exception) - { - if (exception is OperationCanceledException) - throw; + var generatedJobEntity = jobEntityDescription.Generate(); + var sourceFilePath = syntaxTree.GetGeneratedSourceFilePath(compilation.Assembly.Name, GeneratorName, candidate.GetLocation().GetLineSpan().StartLinePosition.Line); + var outputSource = TypeCreationHelpers.GenerateSourceTextForRootNodes(sourceFilePath.FullFilePath, candidate,generatedJobEntity, context.CancellationToken); - context.ReportDiagnostic( - Diagnostic.Create(JobEntityDiagnostics.SGICE003Descriptor, - compilation.SyntaxTrees.First().GetRoot().GetLocation(), - exception.ToUnityPrintableString())); - } + context.AddSource(syntaxTree.GetGeneratedSourceFileName(GeneratorName, candidate), outputSource); + + SourceOutputHelpers.OutputSourceToFile(sourceFilePath.FullFilePath, () => outputSource.ToString()); } - } + catch (Exception exception) + { + if (exception is OperationCanceledException) + throw; - public static class JobEntityDiagnostics - { - public const string ID_SGICE003 = "SGICE003"; - public static readonly DiagnosticDescriptor SGICE003Descriptor - = new DiagnosticDescriptor(ID_SGICE003, "IJobEntity Generator", - "This error indicates a bug in the DOTS source generators. We'd appreciate a bug report (Help -> Report a Bug...). Thanks! Error message: '{0}'.", - JobEntityGenerator.GeneratorName, DiagnosticSeverity.Error, isEnabledByDefault: true, description: ""); + context.ReportDiagnostic( + Diagnostic.Create(JobEntityDiagnostics.SGICE003Descriptor, + compilation.SyntaxTrees.First().GetRoot().GetLocation(), + exception.ToUnityPrintableString())); + } } -} \ No newline at end of file +} + +public static class JobEntityDiagnostics +{ + const string ID_SGICE003 = "SGICE003"; + + public static readonly DiagnosticDescriptor SGICE003Descriptor + = new DiagnosticDescriptor(ID_SGICE003, "IJobEntity Generator", + "This error indicates a bug in the DOTS source generators. We'd appreciate a bug report (Help -> Report a Bug...). Thanks! Error message: '{0}'.", + JobEntityGenerator.GeneratorName, DiagnosticSeverity.Error, isEnabledByDefault: true, description: ""); +} diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityGeneratorErrors.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityGeneratorErrors.cs index 0ca6f22..2d699ff 100644 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityGeneratorErrors.cs +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityGeneratorErrors.cs @@ -2,167 +2,166 @@ using System.Linq; using Microsoft.CodeAnalysis; using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +public static class JobEntityGeneratorErrors { - public static class JobEntityGeneratorErrors - { - const string k_WarningTitle = "IJobEntity Warning"; - const string k_ErrorTitle = "IJobEntity Error"; - - public static void SGJE0003( - ISourceGeneratorDiagnosable context, - Location location, - string parameterName, - string parameterType) - { - context.LogError( - nameof(SGJE0003), - k_ErrorTitle, - $"The parameter '{parameterName}' of type {parameterType} will be ignored.", - location); - } - - public static void SGJE0006(ISourceGeneratorDiagnosable context, Location location, string jobEntityTypeName, string methodSignature, string nonIntegerEntityQueryParameter, string attributeName) - { - context.LogError( - nameof(SGJE0006), - k_ErrorTitle, - $"{jobEntityTypeName}.{methodSignature} accepts a non-integer parameter ('{nonIntegerEntityQueryParameter}') with the [{attributeName}] attribute. " + - $"This is not allowed. The [{attributeName}] attribute may only be applied on integer parameters.", - location); - } - - public static void SGJE0007(ISourceGeneratorDiagnosable context, Location location, string jobEntityTypeName, string methodSignature, string attributeName) - { - context.LogError( - nameof(SGJE0007), - k_ErrorTitle, - $"{jobEntityTypeName}.{methodSignature} accepts more than one integer parameters with the [{attributeName}] attribute. " + - $"This is not allowed. The [{attributeName}] attribute can only be applied EXACTLY ONCE on an integer parameter in {jobEntityTypeName}.{methodSignature}.", - location); - } - - public static void SGJE0008(ISourceGeneratorDiagnosable context, Location location, string jobEntityTypeName, List userDefinedExecuteMethods) - { - context.LogError(nameof(SGJE0008), k_ErrorTitle, - $"You have defined {userDefinedExecuteMethods.Count} Execute() method(s) in {jobEntityTypeName}. " - + "Please define exactly one Execute() method in each IJobEntity type. " - + $"List of perpetrators: {userDefinedExecuteMethods.Select(method => method.ToString()).SeparateByCommaAndSpace()}. " - //+$"With Attributes: {userDefinedExecuteMethods.Select(m => m.GetAttributes().Select(a => a.AttributeClass.ToFullName()).SeparateByComma()).SeparateByComma()}" - , location); - } - - // TODO: Needs test - public static void SGJE0010(ISourceGeneratorDiagnosable context, Location location, string parameter, string parameterType) - { - context.LogError( - nameof(SGJE0010), - k_ErrorTitle, - $"IJobEntity.Execute() parameter '{parameter}' of type {parameterType} is not supported.", - location); - } - - // TODO: Needs test - public static void SGJE0011(ISourceGeneratorDiagnosable diagnosable, Location location, string notValidParam) - { - diagnosable.LogError( - nameof(SGJE0011), - k_ErrorTitle, - $"Execute() parameter '{notValidParam}' is not a supported parameter in an IJobEntity type.", - location); - } - - // TODO: Needs test - public static void SGJE0012(ISourceGeneratorDiagnosable diagnosable, Location location, string parameterType) - { - diagnosable.LogError( - nameof(SGJE0012), - k_ErrorTitle, - $"{parameterType} implements IBufferElementData and must be used as DynamicBuffer<{parameterType}>.", - location); - } - - public static void SGJE0013(ISourceGeneratorDiagnosable diagnosable, Location location, string name, string parameterType) - { - diagnosable.LogError( - nameof(SGJE0013), - k_ErrorTitle, - $"{name} has a shared component, {parameterType}, that uses the Ref keyword, which is not safe for shared components, instead pass by value or use the 'in' keyword.", - location); - } - - public static void SGJE0016(ISourceGeneratorDiagnosable diagnosable, Location location, string name, string parameterType) - { - diagnosable.LogError( - nameof(SGJE0016), - k_ErrorTitle, - $"`{name}` has an empty component of type `{parameterType}`. Empty components must not be used with the `ref` keyword.", - location); - } - - public static void SGJE0017(ISourceGeneratorDiagnosable diagnosable, Location location, string jobName, string typeName) - { - diagnosable.LogError( - nameof(SGJE0017), - k_ErrorTitle, - $"{jobName} has duplicate components of same type {typeName}. Remove all but one to fix.", - location); - } - - public static void SGJE0018(ISourceGeneratorDiagnosable diagnosable, Location location) - { - diagnosable.LogError( - nameof(SGJE0018), - k_ErrorTitle, - "`RefRW`, `RefRO`, `EnabledRefRW`, and `EnabledRefRO` may not be used with `ref` or `in`.", - location); - } - - public static void SGJE0019(ISourceGeneratorDiagnosable diagnosable, Location location, string typeFullName) - { - diagnosable.LogError( - nameof(SGJE0019), - k_ErrorTitle, - $"`{typeFullName}` does not implement `IComponentData`, and thus cannot be used with `EnabledRefRW` or `EnabledRefRO`.", - location); - } - - public static void SGJE0020(ISourceGeneratorDiagnosable diagnosable, Location location, string jobName) - { - diagnosable.LogError( - nameof(SGJE0020), - k_ErrorTitle, - $"`{jobName}` is an IJobEntity job that uses a generic type parameter. This is not currently supported. Please use IJobChunk if you need support for generic parameters.", - location); - } - - public static void SGJE0021(ISourceGeneratorDiagnosable diagnosable, Location location, string parameterName) - { - diagnosable.LogError( - nameof(SGJE0021), - k_ErrorTitle, - $"{parameterName} is an Aspect passed with a `ref` or `in` keyword. Aspects are already act as reference types and should just be passed in by value.", - location); - } - - public static void SGJE0022(ISourceGeneratorDiagnosable diagnosable, Location location, string parameterName) - { - diagnosable.LogError( - nameof(SGJE0022), - k_ErrorTitle, - $"IJobEntity has a Managed component {parameterName} that is used with a `ref` or `in` modifier. Managed components are already passed by reference and are do not have readonly protection. Please pass directly instead, without the `ref` or `in` modifier.", - location); - } - - public static void SGJE0023(ISourceGeneratorDiagnosable diagnosable, Location location, string parameterTypeFullName, string parameterAccessibility, string jobEntityTypeFullName, string jobAccessibility) - { - diagnosable.LogError( - nameof(SGJE0023), - k_ErrorTitle, - "All parameter types in `IJobEntity.Execute()` methods must be as accessible as *or* more accessible than the the `IJobEntity` types in which they are used. " + - $"`{parameterTypeFullName}` is {parameterAccessibility}, but is used as a parameter type in the `Execute()` method of `{jobEntityTypeFullName}`, which is {jobAccessibility}. This is not allowed.", - location); - } + const string k_ErrorTitle = "IJobEntity Error"; + + public static void SGJE0003( + ISourceGeneratorDiagnosable context, + Location location, + string parameterName, + string parameterType) + { + context.LogError( + nameof(SGJE0003), + k_ErrorTitle, + $"The parameter '{parameterName}' of type {parameterType} will be ignored.", + location); + } + + public static void SGJE0006(ISourceGeneratorDiagnosable context, Location location, string jobEntityTypeName, string methodSignature, string nonIntegerEntityQueryParameter, string attributeName) + { + context.LogError( + nameof(SGJE0006), + k_ErrorTitle, + $"{jobEntityTypeName}.{methodSignature} accepts a non-integer parameter ('{nonIntegerEntityQueryParameter}') with the [{attributeName}] attribute. " + + $"This is not allowed. The [{attributeName}] attribute may only be applied on integer parameters.", + location); + } + + public static void SGJE0007(ISourceGeneratorDiagnosable context, Location location, string jobEntityTypeName, string methodSignature, string attributeName) + { + context.LogError( + nameof(SGJE0007), + k_ErrorTitle, + $"{jobEntityTypeName}.{methodSignature} accepts more than one integer parameters with the [{attributeName}] attribute. " + + $"This is not allowed. The [{attributeName}] attribute can only be applied EXACTLY ONCE on an integer parameter in {jobEntityTypeName}.{methodSignature}.", + location); + } + + public static void SGJE0008(ISourceGeneratorDiagnosable context, Location location, string jobEntityTypeName, List userDefinedExecuteMethods) + { + context.LogError(nameof(SGJE0008), k_ErrorTitle, + $"You have defined {userDefinedExecuteMethods.Count} Execute() method(s) in {jobEntityTypeName}. " + + "Please define exactly one Execute() method in each IJobEntity type. " + + $"List of perpetrators: {userDefinedExecuteMethods.Select(method => method.ToString()).SeparateByCommaAndSpace()}. " + //+$"With Attributes: {userDefinedExecuteMethods.Select(m => m.GetAttributes().Select(a => a.AttributeClass.ToFullName()).SeparateByComma()).SeparateByComma()}" + , location); + } + + // TODO: Needs test + public static void SGJE0010(ISourceGeneratorDiagnosable context, Location location, string parameter, string parameterType) + { + context.LogError( + nameof(SGJE0010), + k_ErrorTitle, + $"IJobEntity.Execute() parameter '{parameter}' of type {parameterType} is not supported.", + location); + } + + // TODO: Needs test + public static void SGJE0011(ISourceGeneratorDiagnosable diagnosable, Location location, string notValidParam) + { + diagnosable.LogError( + nameof(SGJE0011), + k_ErrorTitle, + $"Execute() parameter '{notValidParam}' is not a supported parameter in an IJobEntity type.", + location); + } + + // TODO: Needs test + public static void SGJE0012(ISourceGeneratorDiagnosable diagnosable, Location location, string parameterType) + { + diagnosable.LogError( + nameof(SGJE0012), + k_ErrorTitle, + $"{parameterType} implements IBufferElementData and must be used as DynamicBuffer<{parameterType}>.", + location); + } + + public static void SGJE0013(ISourceGeneratorDiagnosable diagnosable, Location location, string name, string parameterType) + { + diagnosable.LogError( + nameof(SGJE0013), + k_ErrorTitle, + $"{name} has a shared component, {parameterType}, that uses the Ref keyword, which is not safe for shared components, instead pass by value or use the 'in' keyword.", + location); + } + + public static void SGJE0016(ISourceGeneratorDiagnosable diagnosable, Location location, string name, string parameterType) + { + diagnosable.LogError( + nameof(SGJE0016), + k_ErrorTitle, + $"`{name}` has an empty component of type `{parameterType}`. Empty components must not be used with the `ref` keyword.", + location); + } + + public static void SGJE0017(ISourceGeneratorDiagnosable diagnosable, Location location, string jobName, string typeName) + { + diagnosable.LogError( + nameof(SGJE0017), + k_ErrorTitle, + $"{jobName} has duplicate components of same type {typeName}. Remove all but one to fix.", + location); + } + + public static void SGJE0018(ISourceGeneratorDiagnosable diagnosable, Location location) + { + diagnosable.LogError( + nameof(SGJE0018), + k_ErrorTitle, + "`RefRW`, `RefRO`, `EnabledRefRW`, and `EnabledRefRO` may not be used with `ref` or `in`.", + location); + } + + public static void SGJE0019(ISourceGeneratorDiagnosable diagnosable, Location location, string typeFullName) + { + diagnosable.LogError( + nameof(SGJE0019), + k_ErrorTitle, + $"`{typeFullName}` does not implement `IComponentData`, and thus cannot be used with `EnabledRefRW` or `EnabledRefRO`.", + location); + } + + public static void SGJE0020(ISourceGeneratorDiagnosable diagnosable, Location location, string jobName) + { + diagnosable.LogError( + nameof(SGJE0020), + k_ErrorTitle, + $"`{jobName}` is an IJobEntity job that uses a generic type parameter. This is not currently supported. Please use IJobChunk if you need support for generic parameters.", + location); + } + + public static void SGJE0021(ISourceGeneratorDiagnosable diagnosable, Location location, string parameterName) + { + diagnosable.LogError( + nameof(SGJE0021), + k_ErrorTitle, + $"{parameterName} is an Aspect passed with a `ref` or `in` keyword. Aspects are already act as reference types and should just be passed in by value.", + location); + } + + public static void SGJE0022(ISourceGeneratorDiagnosable diagnosable, Location location, string parameterName) + { + diagnosable.LogError( + nameof(SGJE0022), + k_ErrorTitle, + $"IJobEntity has a Managed component {parameterName} that is used with a `ref` or `in` modifier. Managed components are already passed by reference and are do not have readonly protection. Please pass directly instead, without the `ref` or `in` modifier.", + location); + } + + public static void SGJE0023(ISourceGeneratorDiagnosable diagnosable, Location location, string parameterTypeFullName, string parameterAccessibility, string jobEntityTypeFullName, string jobAccessibility) + { + diagnosable.LogError( + nameof(SGJE0023), + k_ErrorTitle, + "All parameter types in `IJobEntity.Execute()` methods must be as accessible as *or* more accessible than the the `IJobEntity` types in which they are used. " + + $"`{parameterTypeFullName}` is {parameterAccessibility}, but is used as a parameter type in the `Execute()` method of `{jobEntityTypeFullName}`, which is {jobAccessibility}. This is not allowed.", + location); } } diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityIndentedTextWriterExtensions.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityIndentedTextWriterExtensions.cs new file mode 100644 index 0000000..f6859a1 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityIndentedTextWriterExtensions.cs @@ -0,0 +1,195 @@ +using System.CodeDom.Compiler; +using System.Linq; +using Unity.Entities.SourceGen.Common; + +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +internal static class JobEntityIndentedTextWriterExtensions +{ + internal static void WriteInternalCompilerStruct(this IndentedTextWriter writer, bool requiresEntityManager) + { + writer.WriteLine("/// Internal structure used by the compiler"); + writer.WriteLine("public struct InternalCompiler"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine("[global::System.Diagnostics.Conditional(\"ENABLE_UNITY_COLLECTIONS_CHECKS\")]"); + writer.WriteLine("// scheduleType 0:Run, 1:Schedule, 2:ScheduleParallel"); + writer.WriteLine("public static void CheckForErrors(int scheduleType)"); + writer.WriteLine("{"); + writer.Indent++; + if (requiresEntityManager) + writer.WriteLine("if(scheduleType == 2) throw new global::System.InvalidOperationException(\"Tried to ScheduleParallel a job with a managed execute signature. Please use .Run or .Schedule instead.\");"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + } + + internal static void WriteScheduleAndRunMethods(this IndentedTextWriter writer) + { + writer.WriteLine(@"global::Unity.Jobs.JobHandle __ThrowCodeGenException() => throw new global::System.Exception(""This method should have been replaced by source gen."");"); + writer.WriteLine(); + writer.WriteLine(@"// Emitted to disambiguate scheduling method invocations"); + writer.WriteLine(@"public void Run() => __ThrowCodeGenException();"); + writer.WriteLine(@"public void RunByRef() => __ThrowCodeGenException();"); + writer.WriteLine(@"public void Run(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException();"); + writer.WriteLine(@"public void RunByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException();"); + writer.WriteLine(@"public global::Unity.Jobs.JobHandle Schedule(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException();"); + writer.WriteLine(@"public global::Unity.Jobs.JobHandle ScheduleByRef(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException();"); + writer.WriteLine(@"public global::Unity.Jobs.JobHandle Schedule(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException();"); + writer.WriteLine(@"public global::Unity.Jobs.JobHandle ScheduleByRef(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException();"); + writer.WriteLine(@"public void Schedule() => __ThrowCodeGenException();"); + writer.WriteLine(@"public void ScheduleByRef() => __ThrowCodeGenException();"); + writer.WriteLine(@"public void Schedule(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException();"); + writer.WriteLine(@"public void ScheduleByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException();"); + writer.WriteLine(@"public global::Unity.Jobs.JobHandle ScheduleParallel(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException();"); + writer.WriteLine(@"public global::Unity.Jobs.JobHandle ScheduleParallelByRef(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException();"); + writer.WriteLine(@"public global::Unity.Jobs.JobHandle ScheduleParallel(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException();"); + writer.WriteLine(@"public global::Unity.Jobs.JobHandle ScheduleParallelByRef(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException();"); + writer.WriteLine(@"public global::Unity.Jobs.JobHandle ScheduleParallel(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn, global::Unity.Collections.NativeArray chunkBaseEntityIndices) => __ThrowCodeGenException();"); + writer.WriteLine(@"public global::Unity.Jobs.JobHandle ScheduleParallelByRef(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn, global::Unity.Collections.NativeArray chunkBaseEntityIndices) => __ThrowCodeGenException();"); + writer.WriteLine(@"public void ScheduleParallel() => __ThrowCodeGenException();"); + writer.WriteLine(@"public void ScheduleParallelByRef() => __ThrowCodeGenException();"); + writer.WriteLine(@"public void ScheduleParallel(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException();"); + writer.WriteLine(@"public void ScheduleParallelByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException();"); + } + + internal static void WriteExecuteMethod(this IndentedTextWriter writer, + bool inheritsFromBeginEndChunk, + bool hasEnableableComponent, + JobEntityParam[] userExecuteMethodParameters) + { + writer.WriteLine("[global::System.Runtime.CompilerServices.CompilerGenerated]"); + writer.WriteLine("public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkIndexInQuery, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask)"); + writer.WriteLine("{"); + writer.Indent++; + + if (inheritsFromBeginEndChunk) + { + writer.WriteLine("var shouldExecuteChunk = OnChunkBegin(in chunk, chunkIndexInQuery, useEnabledMask, in chunkEnabledMask);"); + writer.WriteLine("if (shouldExecuteChunk)"); + writer.WriteLine("{"); + writer.Indent++; + } + + foreach (var param in userExecuteMethodParameters) + { + if (!string.IsNullOrEmpty(param.VariableDeclarationAtStartOfExecuteMethod)) + writer.WriteLine(param.VariableDeclarationAtStartOfExecuteMethod); + } + + writer.WriteLine("int chunkEntityCount = chunk.Count;"); + writer.WriteLine("int matchingEntityCount = 0;"); + writer.WriteLine(); + + if (hasEnableableComponent) + { + writer.WriteLine("if (!useEnabledMask)"); + writer.WriteLine("{"); + writer.Indent++; + } + + writer.WriteLine("for(int entityIndexInChunk = 0; entityIndexInChunk < chunkEntityCount; ++entityIndexInChunk)"); + writer.WriteLine("{"); + writer.Indent++; + + foreach (var param in userExecuteMethodParameters) + { + if (param.RequiresExecuteMethodArgumentSetup) + writer.WriteLine(param.ExecuteMethodArgumentSetup); + } + + writer.WriteLine($"Execute({userExecuteMethodParameters.Select(param => param.ExecuteMethodArgumentValue).SeparateByComma()});"); + writer.WriteLine("matchingEntityCount++;"); + writer.Indent--; + writer.WriteLine("}"); + + if (!hasEnableableComponent) + return; + + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("else"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine( + "int edgeCount = global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong0 ^ (chunkEnabledMask.ULong0 << 1)) " + + "+ global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong1 ^ (chunkEnabledMask.ULong1 << 1)) - 1;"); + writer.WriteLine("bool useRanges = edgeCount <= 4;"); + writer.WriteLine("if (useRanges)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("int entityIndexInChunk = 0;"); + writer.WriteLine("int chunkEndIndex = 0;"); + writer.WriteLine(); + writer.WriteLine("while (global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeTryGetNextEnabledBitRange(chunkEnabledMask, chunkEndIndex, out entityIndexInChunk, out chunkEndIndex))"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("while (entityIndexInChunk < chunkEndIndex)"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var param in userExecuteMethodParameters) + { + if (param.RequiresExecuteMethodArgumentSetup) + writer.WriteLine(param.ExecuteMethodArgumentSetup); + } + writer.WriteLine($"Execute({userExecuteMethodParameters.Select(param => param.ExecuteMethodArgumentValue).SeparateByComma()});"); + writer.WriteLine("entityIndexInChunk++;"); + writer.WriteLine("matchingEntityCount++;"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("else"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("ulong mask64 = chunkEnabledMask.ULong0;"); + writer.WriteLine("int count = global::Unity.Mathematics.math.min(64, chunkEntityCount);"); + writer.WriteLine("for (int entityIndexInChunk = 0; entityIndexInChunk < count; ++entityIndexInChunk)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("if ((mask64 & 1) != 0)"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var param in userExecuteMethodParameters) + { + if (param.RequiresExecuteMethodArgumentSetup) + writer.WriteLine(param.ExecuteMethodArgumentSetup); + } + writer.WriteLine($"Execute({userExecuteMethodParameters.Select(param => param.ExecuteMethodArgumentValue).SeparateByComma()});"); + writer.WriteLine("matchingEntityCount++;"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("mask64 >>= 1;"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("mask64 = chunkEnabledMask.ULong1;"); + writer.WriteLine("for (int entityIndexInChunk = 64; entityIndexInChunk < chunkEntityCount; ++entityIndexInChunk)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("if ((mask64 & 1) != 0)"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var param in userExecuteMethodParameters) + { + if (param.RequiresExecuteMethodArgumentSetup) + writer.WriteLine(param.ExecuteMethodArgumentSetup); + } + writer.WriteLine($"Execute({userExecuteMethodParameters.Select(param => param.ExecuteMethodArgumentValue).SeparateByComma()});"); + writer.WriteLine("matchingEntityCount++;"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("mask64 >>= 1;"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.JobEntityInstanceInfo.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.JobEntityInstanceInfo.cs new file mode 100644 index 0000000..b41d2ae --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.JobEntityInstanceInfo.cs @@ -0,0 +1,292 @@ +#nullable enable +using System; +using System.CodeDom.Compiler; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; + +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +public partial class JobEntityModule +{ + internal struct JobEntityInstanceInfo : ISystemCandidate + { + internal JobEntityCandidate Candidate { get; private set; } + internal bool IsExtensionMethodUsed { get; private set; } + TypeInfo _typeInfo; + + public string CandidateTypeName => Candidate.CandidateTypeName; + public SyntaxNode Node => Candidate.Node; + + public static bool TryCreate(ref SystemDescription systemDescription, JobEntityCandidate candidate, out JobEntityInstanceInfo result) + { + result = new JobEntityInstanceInfo + { + Candidate = candidate + }; + + // Checks if the Candidate is JobEntity and Get Type info + ExpressionSyntax? jobEntityInstance = null; + + switch (candidate.MemberAccessExpressionSyntax.Expression) + { + case IdentifierNameSyntax identifierNameSyntax: + jobEntityInstance = identifierNameSyntax; + break; + case ObjectCreationExpressionSyntax objectCreationExpressionSyntax: + jobEntityInstance = objectCreationExpressionSyntax; + break; + } + + if (jobEntityInstance == null) + return false; + + result._typeInfo = systemDescription.SemanticModel.GetTypeInfo(jobEntityInstance); + + (bool isJobEntity, result.IsExtensionMethodUsed) = DoesTypeInheritJobEntityAndUseExtensionMethod(result._typeInfo); + return isJobEntity; + } + + public (bool Success, ObjectCreationExpressionSyntax? ObjectCreationExpressionSyntax, IdentifierNameSyntax? IdentifierNameSyntax, int JobArgumentIndexInExtensionMethod) + TryGetJobArgumentUsedInSchedulingInvocation() + { + var result = Candidate.MemberAccessExpressionSyntax.Expression; + + if (IsExtensionMethodUsed) + { + // Find JobData Syntax Passed To ExtensionMethod + int jobDataArgumentIndex = FindJobDataArgumentIndex(Candidate.Invocation); + if (jobDataArgumentIndex == -1) + return default; + + result = Candidate.Invocation.ArgumentList.Arguments[jobDataArgumentIndex].Expression; + return result switch + { + ObjectCreationExpressionSyntax objectCreationExpressionSyntax => (true, objectCreationExpressionSyntax, null, jobDataArgumentIndex), + IdentifierNameSyntax identifierNameSyntax => (true, null, identifierNameSyntax, jobDataArgumentIndex), + _ => default + }; + } + + return result switch + { + ObjectCreationExpressionSyntax objectCreationExpressionSyntax => (true, objectCreationExpressionSyntax, null, -1), + IdentifierNameSyntax identifierNameSyntax => (true, null, identifierNameSyntax, -1), + _ => default + }; + } + public string? GetAndAddScheduleExpression( + ref SystemDescription systemDescription, + int uniqueId, + int jobArgumentIndexInExtensionMethod, + string schedulingJobEntityInstanceArg, + string? userDefinedQueryArg, + string? userDefinedDependencyArg) + { + var jobEntityType = (INamedTypeSymbol)_typeInfo.Type!; + + // Update Job Info if Extension method is used + if (IsExtensionMethodUsed) + { + // Get JobEntity Symbol Passed To ExtensionMethod - Using Candidate for Semantic Reference + jobEntityType = Candidate.Invocation.ArgumentList.Arguments[jobArgumentIndexInExtensionMethod].Expression switch + { + ObjectCreationExpressionSyntax objectCreationExpressionSyntax + when systemDescription.SemanticModel.GetSymbolInfo(objectCreationExpressionSyntax).Symbol is IMethodSymbol + { + ReceiverType: INamedTypeSymbol namedTypeSymbol + } => namedTypeSymbol, + + IdentifierNameSyntax identifierNameSyntax + when systemDescription.SemanticModel.GetSymbolInfo(identifierNameSyntax).Symbol is ILocalSymbol + { + Type: INamedTypeSymbol namedTypeSymbol + } => namedTypeSymbol, + _ => null + }; + } + + // Get Additional info + var scheduleMode = ScheduleModeHelpers.GetScheduleModeFromNameOfMemberAccess(Candidate.MemberAccessExpressionSyntax); + if (!systemDescription.TryGetSystemStateParameterName(Candidate, out var systemStateExpression)) + return null; + + // Get or create `__TypeHandle.MyJob__JobEntityHandle` + var jobEntityHandle = systemDescription.QueriesAndHandles.GetOrCreateJobEntityHandle(jobEntityType, userDefinedQueryArg == null); + var jobEntityHandleAccess = $"__TypeHandle.{jobEntityHandle}"; + + // is DynamicQuery ? `userQuery` : `__TypeHandle.MyJob__JobEntityHandle.DefaultQuery` + var entityQuery = userDefinedQueryArg ?? $"{jobEntityHandleAccess}.DefaultQuery"; + + var schedulingMethodWriter = new SchedulingMethodWriter + { + ScheduleMode = scheduleMode, + MethodId = uniqueId, + FullTypeName = jobEntityType.ToFullName(), + JobEntityHandle = jobEntityHandle, + CheckQuery = systemDescription.PreprocessorInfo.IsUnityCollectionChecksEnabled || systemDescription.PreprocessorInfo.IsDotsDebugMode + }; + + systemDescription.NewMiscellaneousMembers.Add(schedulingMethodWriter); + + return new SchedulingExpressionCreateInfo( + schedulingMethodWriter.DefaultMethodName, + scheduleMode, + hasUserDefinedQuery: userDefinedQueryArg != null, + schedulingJobEntityInstanceArg, + entityQuery, + userDefinedDependencyArg, + systemStateExpression + ).Write(); + } + + private static int FindJobDataArgumentIndex(InvocationExpressionSyntax invocationExpression) + { + int jobDataArgumentIndex = -1; + + for (var argumentIndex = 0; argumentIndex < invocationExpression.ArgumentList.Arguments.Count; argumentIndex++) + { + var argument = invocationExpression.ArgumentList.Arguments[argumentIndex]; + if (argument.NameColon == null) + jobDataArgumentIndex = argumentIndex; + else if (argument.NameColon.Name.Identifier.ValueText == "jobData") + jobDataArgumentIndex = argumentIndex; + } + + return jobDataArgumentIndex; + } + + enum ArgumentType + { + EntityQuery, + Dependency, + JobEntity + } + + internal static (ExpressionSyntax? UserDefinedEntityQuery, ExpressionSyntax? UserDefinedDependency) + GetUserDefinedQueryAndDependency(ref SystemDescription context, bool isExtensionMethodUsed, InvocationExpressionSyntax schedulingInvocation) + { + ExpressionSyntax? entityQueryArgument = null; + ExpressionSyntax? dependencyArgument = null; + + for (var i = 0; i < schedulingInvocation.ArgumentList.Arguments.Count; i++) + { + var arg = schedulingInvocation.ArgumentList.Arguments[i]; + + var type = arg.NameColon == null + ? ParseUnnamedArgument(ref context, i, isExtensionMethodUsed, arg) + : ParseNamedArgument(arg); + + switch (type) + { + case ArgumentType.EntityQuery: + entityQueryArgument = arg.Expression; + continue; + case ArgumentType.Dependency: + dependencyArgument = arg.Expression; + continue; + } + } + + return (entityQueryArgument, dependencyArgument); + } + + static ArgumentType ParseUnnamedArgument(ref SystemDescription context, int argumentPosition, bool isExtensionMethodUsed, ArgumentSyntax semanticOriginalArgument) + { + switch (argumentPosition+(isExtensionMethodUsed?0:1)) + { + case 0: + return ArgumentType.JobEntity; + case 1: // Could be EntityQuery or dependsOn + { + if (semanticOriginalArgument.Expression is DefaultExpressionSyntax {Type: var defaultType}) + return defaultType.ToString().Contains("JobHandle") ? ArgumentType.Dependency : ArgumentType.EntityQuery; + + return context.SemanticModel.GetTypeInfo(semanticOriginalArgument.Expression).Type.ToFullName() == "global::Unity.Entities.EntityQuery" + ? ArgumentType.EntityQuery : ArgumentType.Dependency; + } + case 2: // dependsOn + return ArgumentType.Dependency; + } + + throw new ArgumentOutOfRangeException(); + } + + static ArgumentType ParseNamedArgument(ArgumentSyntax argumentSyntax) + => argumentSyntax.NameColon?.Name.Identifier.ValueText switch + { + "query" => ArgumentType.EntityQuery, + "dependsOn" => ArgumentType.Dependency, + "jobData" => ArgumentType.JobEntity, + _ => throw new ArgumentOutOfRangeException() + }; + + struct SchedulingMethodWriter : IMemberWriter + { + public ScheduleMode ScheduleMode; + public int MethodId; + public string FullTypeName; + public string JobEntityHandle; + public bool CheckQuery; + + public string DefaultMethodName => $"__ScheduleViaJobChunkExtension_{MethodId}"; + + public void WriteTo(IndentedTextWriter writer) + { + var containsReturn = !ScheduleMode.IsRun(); + + var methodWithArguments = ScheduleMode.GetScheduleMethodWithArguments(); + var returnType = containsReturn ? "global::Unity.Jobs.JobHandle" : "void"; + var returnExpression = $"{"return ".EmitIfTrue(containsReturn)}__TypeHandle.{JobEntityHandle}.{methodWithArguments}"; + var refType = "ref ".EmitIfTrue(ScheduleMode.IsByRef()); + + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine($"{returnType} {DefaultMethodName}({refType}{FullTypeName} job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency, ref global::Unity.Entities.SystemState state, bool hasUserDefinedQuery)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"{FullTypeName}.InternalCompiler.CheckForErrors({ScheduleMode.GetScheduleTypeAsNumber()});"); + + if (CheckQuery) + { + writer.WriteLine("if (Unity.Burst.CompilerServices.Hint.Unlikely(hasUserDefinedQuery))"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"int requiredComponentCount = {FullTypeName}.InternalCompilerQueryAndHandleData.GetRequiredComponentTypeCount();"); + writer.WriteLine("global::System.Span requiredComponentTypes = stackalloc Unity.Entities.ComponentType[requiredComponentCount];"); + writer.WriteLine($"{FullTypeName}.InternalCompilerQueryAndHandleData.AddRequiredComponentTypes(ref requiredComponentTypes);"); + writer.WriteLine(); + writer.WriteLine($"if (!{FullTypeName}.InternalCompilerQueryAndHandleData.QueryHasRequiredComponentsForExecuteMethodToRun(ref query, ref requiredComponentTypes))"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.InvalidOperationException("); + writer.WriteLine($"\"When scheduling an instance of `{FullTypeName}` with a custom query, the query must (at the very minimum) contain all the components required for `{FullTypeName}.Execute()` to run.\");"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + } + + if (containsReturn) + writer.WriteLine("dependency = "); + writer.WriteLine($"__TypeHandle.{JobEntityHandle}.UpdateBaseEntityIndexArray(ref job, query, "); + + if (containsReturn) + writer.Write("dependency, "); + + writer.Write("ref state);"); + writer.WriteLine($"__TypeHandle.{JobEntityHandle}.AssignEntityManager(ref job, state.EntityManager);"); + writer.WriteLine($"__TypeHandle.{JobEntityHandle}.__TypeHandle.Update(ref state);"); + writer.WriteLine($"{returnExpression};"); + writer.Indent--; + writer.WriteLine("}"); + } + } + + static (bool IsKnownCandidate, bool IsExtensionMethodUsed) DoesTypeInheritJobEntityAndUseExtensionMethod(TypeInfo typeInfo) => + typeInfo.Type.InheritsFromInterface("Unity.Entities.IJobEntity") + ? (IsKnownCandidate: true, IsExtensionMethodUsed: false) // IsExtensionMethodUsed is ignored if IsCandidate is false, so we don't need to test the same thing twice + : (IsKnownCandidate: typeInfo.Type.ToFullName() == "global::Unity.Entities.IJobEntityExtensions", IsExtensionMethodUsed: true); + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.KnownJobEntityInfo.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.KnownJobEntityInfo.cs deleted file mode 100644 index f46f480..0000000 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.KnownJobEntityInfo.cs +++ /dev/null @@ -1,304 +0,0 @@ -#nullable enable -using System; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.Common; -using Unity.Entities.SourceGen.SystemGenerator.Common; - -namespace Unity.Entities.SourceGen.JobEntity -{ - public partial class JobEntityModule - { - struct KnownJobEntityInfo : ISystemCandidate - { - JobEntityCandidate m_Candidate; - TypeInfo m_TypeInfo; - bool m_IsExtensionMethodUsed; - - public static bool TryCreate(ref SystemDescription systemDescription, JobEntityCandidate candidate, out KnownJobEntityInfo result) - { - result = new KnownJobEntityInfo - { - m_Candidate = candidate - }; - - // Checks if the Candidate is JobEntity and Get Type info - ExpressionSyntax? jobEntityInstance = candidate.MemberAccessExpressionSyntax.Expression as IdentifierNameSyntax; - jobEntityInstance ??= candidate.MemberAccessExpressionSyntax.Expression as ObjectCreationExpressionSyntax; - - if (jobEntityInstance == null) - return false; - - result.m_TypeInfo = systemDescription.SemanticModel.GetTypeInfo(jobEntityInstance); - - bool isJobEntity; - (isJobEntity, result.m_IsExtensionMethodUsed) = DoesTypeInheritJobEntityAndUseExtensionMethod(result.m_TypeInfo); - - return isJobEntity; - } - - static (bool IsKnownCandidate, bool IsExtensionMethodUsed) DoesTypeInheritJobEntityAndUseExtensionMethod(TypeInfo typeInfo) => - typeInfo.Type.InheritsFromInterface("Unity.Entities.IJobEntity") - ? (IsKnownCandidate: true, IsExtensionMethodUsed: false) // IsExtensionMethodUsed is ignored if IsCandidate is false, so we don't need to test the same thing twice - : (IsKnownCandidate: typeInfo.Type.ToFullName() == "global::Unity.Entities.IJobEntityExtensions", IsExtensionMethodUsed: true); - - /// Replaced Invocation Scheduling Expression, null if invalid - public ExpressionSyntax? GetAndAddScheduleExpression(ref SystemDescription systemDescription, int i, MemberAccessExpressionSyntax currentSchedulingNode) - { - // Get Job Info - var jobArgumentUsedInSchedulingMethod = currentSchedulingNode.Expression; - var jobEntityType = (INamedTypeSymbol)m_TypeInfo.Type!; - - // Update Job Info if Extension method is used - if (m_IsExtensionMethodUsed) - { - // Silent fail, compiler will give correct CSC error on our behalf. - if (!(currentSchedulingNode.Parent is InvocationExpressionSyntax invocationExpression)) - return null; - - // Find JobData Syntax Passed To ExtensionMethod - var jobDataArgumentIndex = -1; - for (var argumentIndex = 0; argumentIndex < invocationExpression.ArgumentList.Arguments.Count; argumentIndex++) - { - var argument = invocationExpression.ArgumentList.Arguments[argumentIndex]; - if (argument.NameColon == null) - jobDataArgumentIndex = argumentIndex; - else if (argument.NameColon.Name.Identifier.ValueText == "jobData") - jobDataArgumentIndex = argumentIndex; - } - if (jobDataArgumentIndex == -1) - return null; - jobArgumentUsedInSchedulingMethod = invocationExpression.ArgumentList.Arguments[jobDataArgumentIndex].Expression; - - // Get JobEntity Symbol Passed To ExtensionMethod - Using Candidate for Semantic Reference - jobEntityType = m_Candidate.Invocation!.ArgumentList.Arguments[jobDataArgumentIndex].Expression switch - { - ObjectCreationExpressionSyntax objectCreationExpressionSyntax - when systemDescription.SemanticModel.GetSymbolInfo(objectCreationExpressionSyntax).Symbol is IMethodSymbol - { - ReceiverType: INamedTypeSymbol namedTypeSymbol - } => namedTypeSymbol, - - IdentifierNameSyntax identifierNameSyntax - when systemDescription.SemanticModel.GetSymbolInfo(identifierNameSyntax).Symbol is ILocalSymbol - { - Type: INamedTypeSymbol namedTypeSymbol - } => namedTypeSymbol, - _ => null - }; - } - - // Get Additional info - var scheduleMode = ScheduleModeHelpers.GetScheduleModeFromNameOfMemberAccess(m_Candidate.MemberAccessExpressionSyntax); - if (!systemDescription.TryGetSystemStateParameterName(m_Candidate, out var systemStateExpression)) - return null; - - var (userDefinedQuery, userDefinedDependency) = - GetUserDefinedQueryAndDependency( - ref systemDescription, - m_IsExtensionMethodUsed, - (currentSchedulingNode.Parent as InvocationExpressionSyntax)!, - m_Candidate.Invocation!, - scheduleMode); - - // Get or create `__TypeHandle.MyJob__JobEntityHandle` - var jobEntityHandle = systemDescription.HandlesDescription.GetOrCreateJobEntityHandle(jobEntityType, userDefinedQuery == null); - var jobEntityHandleExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("__TypeHandle"), SyntaxFactory.IdentifierName(jobEntityHandle)); - - // is DynamicQuery ? `userQuery` : `__TypeHandle.MyJob__JobEntityHandle.DefaultQuery` - var entityQuery = userDefinedQuery ?? SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, jobEntityHandleExpression, SyntaxFactory.IdentifierName("DefaultQuery")); - var scheduleMethodName = AddSchedulingMethodToSystem(ref systemDescription, scheduleMode, i, jobEntityType.ToFullName(), jobEntityHandle); - - return new SchedulingExpressionCreateInfo( - scheduleMethodName, - scheduleMode, - hasUserDefinedQuery: userDefinedQuery != null, - jobArgumentUsedInSchedulingMethod, - entityQuery, - userDefinedDependency, - systemStateExpression - ).ToExpressionSyntax(); - } - - enum ArgumentType - { - EntityQuery, - Dependency, - JobEntity - } - - static (ExpressionSyntax? UserDefinedEntityQuery, ExpressionSyntax? UserDefinedDependency) - GetUserDefinedQueryAndDependency(ref SystemDescription context, bool isExtensionMethodUsed, InvocationExpressionSyntax giveBackInvocation, InvocationExpressionSyntax semanticOriginalInvocation, ScheduleMode scheduleMode) - { - ExpressionSyntax? entityQueryArgument = null; - ExpressionSyntax? dependencyArgument = null; - - for (var i = 0; i < semanticOriginalInvocation.ArgumentList.Arguments.Count; i++) - { - var semanticOriginalArgument = semanticOriginalInvocation.ArgumentList.Arguments[i]; - var giveBackArgument = giveBackInvocation.ArgumentList.Arguments[i]; - - var type = semanticOriginalArgument.NameColon == null - ? ParseUnnamedArgument(ref context, i) - : ParseNamedArgument(); - - ArgumentType ParseUnnamedArgument(ref SystemDescription context, int argumentPosition) - { - switch (argumentPosition+(isExtensionMethodUsed?0:1)) - { - case 0: - return ArgumentType.JobEntity; - case 1: // Could be EntityQuery or dependsOn - { - if (semanticOriginalArgument.Expression is DefaultExpressionSyntax {Type: var defaultType}) - return defaultType.ToString().Contains("JobHandle") ? ArgumentType.Dependency : ArgumentType.EntityQuery; - - return context.SemanticModel.GetTypeInfo(semanticOriginalArgument.Expression).Type.ToFullName() == "global::Unity.Entities.EntityQuery" - ? ArgumentType.EntityQuery : ArgumentType.Dependency; - } - case 2: // dependsOn - return ArgumentType.Dependency; - } - - throw new ArgumentOutOfRangeException(); - } - - ArgumentType ParseNamedArgument() - => semanticOriginalArgument.NameColon.Name.Identifier.ValueText switch - { - "query" => ArgumentType.EntityQuery, - "dependsOn" => ArgumentType.Dependency, - "jobData" => ArgumentType.JobEntity, - _ => throw new ArgumentOutOfRangeException() - }; - - switch (type) - { - case ArgumentType.EntityQuery: - entityQueryArgument = giveBackArgument.Expression; - continue; - case ArgumentType.Dependency: - dependencyArgument = giveBackArgument.Expression; - continue; - } - } - - return (entityQueryArgument, dependencyArgument); - } - - static string AddSchedulingMethodToSystem(ref SystemDescription systemDescription, ScheduleMode scheduleMode, int methodId, string fullTypeName, string jobEntityHandle) - { - var containsReturn = !scheduleMode.IsRun(); - var methodName = $"__ScheduleViaJobChunkExtension_{methodId}"; - var methodWithArguments = scheduleMode.GetScheduleMethodWithArguments(); - var returnType = containsReturn ? "global::Unity.Jobs.JobHandle" : "void"; - var returnExpression = $"{"return ".EmitIfTrue(containsReturn)}__TypeHandle.{jobEntityHandle}.{methodWithArguments}"; - var refType = "ref ".EmitIfTrue(scheduleMode.IsByRef()); - - var method = - $@"[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - {returnType} {methodName}({refType}{fullTypeName} job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency, ref global::Unity.Entities.SystemState state, bool hasUserDefinedQuery) - {{ - {fullTypeName}.InternalCompiler.CheckForErrors({scheduleMode.GetScheduleTypeAsNumber()}); - - {PossiblyThrowUserDefinedQueryException(fullTypeName, checkQuery: systemDescription.IsUnityCollectionChecksEnabled || systemDescription.IsDotsDebugMode)} - - {"dependency = ".EmitIfTrue(containsReturn)}__TypeHandle.{jobEntityHandle}.UpdateBaseEntityIndexArray(ref job, query, {"dependency, ".EmitIfTrue(containsReturn)}ref state); - __TypeHandle.{jobEntityHandle}.AssignEntityManager(ref job, state.EntityManager); - __TypeHandle.{jobEntityHandle}.__TypeHandle.Update(ref state); - {returnExpression}; - }}"; - - systemDescription.NewMiscellaneousMembers.Add(SyntaxFactory.ParseMemberDeclaration(method)); - return methodName; - } - - static string PossiblyThrowUserDefinedQueryException(string jobTypeFullName, bool checkQuery) - { - if (!checkQuery) - return string.Empty; - - return - $@"if (Unity.Burst.CompilerServices.Hint.Unlikely(hasUserDefinedQuery)) - {{ - int requiredComponentCount = {jobTypeFullName}.InternalCompilerQueryAndHandleData.GetRequiredComponentTypeCount(); - global::System.Span requiredComponentTypes = stackalloc Unity.Entities.ComponentType[requiredComponentCount]; - - {jobTypeFullName}.InternalCompilerQueryAndHandleData.AddRequiredComponentTypes(ref requiredComponentTypes); - - if (!{jobTypeFullName}.InternalCompilerQueryAndHandleData.QueryHasRequiredComponentsForExecuteMethodToRun(ref query, ref requiredComponentTypes)) - {{ - throw new global::System.InvalidOperationException( - ""When scheduling an instance of `{jobTypeFullName}` with a custom query, the query must (at the very minimum) contain all the components required for `{jobTypeFullName}.Execute()` to run.""); - }} - }}"; - } - - public string CandidateTypeName => m_Candidate.CandidateTypeName; - public SyntaxNode Node => m_Candidate.Node; - } - - readonly ref struct SchedulingExpressionCreateInfo - { - readonly string m_SchedulingMethodName; - readonly ScheduleMode m_ScheduleMode; - readonly ExpressionSyntax m_Job; - readonly ExpressionSyntax m_QueryToUse; - readonly ExpressionSyntax? m_UserDefinedDependency; - readonly ExpressionSyntax m_SystemStateExpression; - readonly bool m_HasUserDefinedQuery; - - public SchedulingExpressionCreateInfo( - string schedulingMethodName, - ScheduleMode scheduleMode, - bool hasUserDefinedQuery, - ExpressionSyntax job, - ExpressionSyntax queryToUse, - ExpressionSyntax? userDefinedDependency, - ExpressionSyntax systemStateExpression) - { - m_SchedulingMethodName = schedulingMethodName; - m_ScheduleMode = scheduleMode; - m_Job = job; - m_HasUserDefinedQuery = hasUserDefinedQuery; - m_QueryToUse = queryToUse; - m_UserDefinedDependency = userDefinedDependency; - m_SystemStateExpression = systemStateExpression; - } - - /// - /// Creates ExpressionSyntax for scheduling, e.g. `someSchedulingMethod(ref SomeJob, someQuery, someDependency, ref systemState)` - /// - /// The created ExpressionSyntax - public ExpressionSyntax ToExpressionSyntax() - { - // Dependency: `systemState.Dependency` - var dependencyNode = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, m_SystemStateExpression, SyntaxFactory.IdentifierName("Dependency")); - - // Arguments: `ref SomeJob, someQuery, someDependency, ref systemState` - var argumentList = SyntaxFactory.SeparatedList(new[] - { - SyntaxFactory.Argument(null, m_ScheduleMode.IsByRef() ? SyntaxFactory.Token(SyntaxKind.RefKeyword) : SyntaxFactory.Token(SyntaxKind.None), m_Job.WithoutLeadingTrivia()), - SyntaxFactory.Argument(m_QueryToUse), - SyntaxFactory.Argument(m_UserDefinedDependency ?? dependencyNode), - SyntaxFactory.Argument(null, SyntaxFactory.Token(SyntaxKind.RefKeyword), m_SystemStateExpression), - SyntaxFactory.Argument(m_HasUserDefinedQuery ? SyntaxFactory.LiteralExpression(SyntaxKind.TrueLiteralExpression) : SyntaxFactory.LiteralExpression(SyntaxKind.FalseLiteralExpression)) - }); - - // Finalize invocation: `someSchedulingMethod(ref SomeJob, someQuery, someDependency, ref systemState)` - ExpressionSyntax replaceToNode = - SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(m_SchedulingMethodName), SyntaxFactory.ArgumentList(argumentList)); - - // Maybe prefix assignment: `systemState.Dependency = someSchedulingMethod(ref SomeJob, someQuery, someDependency, ref systemState)` - var doStateDepsAssignment = m_UserDefinedDependency == null; - doStateDepsAssignment &= !m_ScheduleMode.IsRun(); - if (doStateDepsAssignment) - replaceToNode = SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, dependencyNode, replaceToNode); - - // Return with trivia from job - important when its called as extension method - return replaceToNode.WithLeadingTrivia(m_Job.GetLeadingTrivia()); - } - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.Rewriter.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.Rewriter.cs deleted file mode 100644 index 0d461d8..0000000 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.Rewriter.cs +++ /dev/null @@ -1,81 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.SystemGenerator.Common; - -namespace Unity.Entities.SourceGen.JobEntity -{ - public partial class JobEntityModule - { - class Rewriter : SystemRewriter - { - readonly Dictionary m_ScheduleToKnownCandidate; - readonly IReadOnlyCollection m_KnownJobEntityInfos; - SystemDescription m_SystemDescription; - - bool m_HasChangedMember; - SyntaxNode m_ReplacementNode; - SyntaxNode m_NodeToReplace; - - public override IEnumerable NodesToTrack => m_KnownJobEntityInfos.Select(info => info.Node); - public Rewriter(ref SystemDescription systemDescription, IReadOnlyCollection knownJobEntityInfos) - { - m_SystemDescription = systemDescription; - m_KnownJobEntityInfos = knownJobEntityInfos; - m_ScheduleToKnownCandidate = new Dictionary(); - } - - // By the time this method is invoked, the system has already been rewritten at least once. - // In other words, the `systemRootNode` argument passed to this method is the root node of the REWRITTEN system -- - // i.e., a copy of the original system with changes applied. - public override SyntaxNode VisitTrackedSystem(SyntaxNode systemRootNode, string originalFilePath) - { - m_OriginalFilePath = originalFilePath; - - foreach (var knownJobEntityInfo in m_KnownJobEntityInfos) - { - var nodeInNewestTreeVersion = systemRootNode.GetCurrentNodes(knownJobEntityInfo.Node).FirstOrDefault() ?? knownJobEntityInfo.Node; - m_ScheduleToKnownCandidate[nodeInNewestTreeVersion] = knownJobEntityInfo; - } - - return Visit(systemRootNode); - } - - int m_UniqueIndex; - public override SyntaxNode Visit(SyntaxNode syntaxNode) - { - if (syntaxNode == null) - return null; - - var replacedNodeAndChildren = base.Visit(syntaxNode); - - // If the current node is a node we want to replace -- e.g. `someJob.Schedule()` - if (replacedNodeAndChildren is MemberAccessExpressionSyntax newestSchedulingNode && m_ScheduleToKnownCandidate.TryGetValue(syntaxNode, out var description)) - { - // Replace the current node - var replacementNode = description.GetAndAddScheduleExpression(ref m_SystemDescription, m_UniqueIndex++, newestSchedulingNode); - if (replacementNode != null) - { - m_ReplacementNode = replacementNode; - m_NodeToReplace = syntaxNode.Parent; - } - } - - if (syntaxNode == m_NodeToReplace) - { - replacedNodeAndChildren = m_ReplacementNode; - m_HasChangedMember = true; - } - - // If we have performed any replacements, we need to update the `RewrittenMemberHashCodeToSyntaxNode` dictionary accordingly - if (replacedNodeAndChildren is MemberDeclarationSyntax memberDeclarationSyntax && m_HasChangedMember) - { - RecordChangedMember(memberDeclarationSyntax); - m_HasChangedMember = false; - } - return replacedNodeAndChildren; - } - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.SchedulingExpressionCreateInfo.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.SchedulingExpressionCreateInfo.cs new file mode 100644 index 0000000..0b93a8a --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.SchedulingExpressionCreateInfo.cs @@ -0,0 +1,56 @@ +#nullable enable +using System.Text; +using Microsoft.CodeAnalysis.CSharp.Syntax; + +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +public partial class JobEntityModule +{ + internal readonly ref struct SchedulingExpressionCreateInfo + { + readonly string _schedulingMethodName; + readonly ScheduleMode _scheduleMode; + readonly string _jobArg; + readonly string _queryToUse; + readonly string? _userDefinedDependency; + readonly ExpressionSyntax _systemStateExpression; + readonly bool _hasUserDefinedQuery; + + public SchedulingExpressionCreateInfo( + string schedulingMethodName, + ScheduleMode scheduleMode, + bool hasUserDefinedQuery, + string jobArg, + string queryToUse, + string? userDefinedDependency, + ExpressionSyntax systemStateExpression) + { + _schedulingMethodName = schedulingMethodName; + _scheduleMode = scheduleMode; + _jobArg = jobArg; + _hasUserDefinedQuery = hasUserDefinedQuery; + _queryToUse = queryToUse; + _userDefinedDependency = userDefinedDependency; + _systemStateExpression = systemStateExpression; + } + + public string Write() + { + var stringBuilder = new StringBuilder(); + + // Maybe prefix assignment: `systemState.Dependency = someSchedulingMethod(ref SomeJob, someQuery, someDependency, ref systemState)` + var assignStateDependency = _userDefinedDependency == null & !_scheduleMode.IsRun(); + if (assignStateDependency) + stringBuilder.Append($"{_systemStateExpression}.Dependency = "); + + string jobArg = $"{(_scheduleMode.IsByRef() ? $"ref {_jobArg}" : $"{_jobArg}")}"; + string queryArg = _queryToUse; + string dependencyArg = $"{_userDefinedDependency ?? $"{_systemStateExpression}.Dependency"}"; + string systemStateArg = $"ref {_systemStateExpression}"; + string hasUserDefinedQueryArg = _hasUserDefinedQuery ? "true" : "false"; + + stringBuilder.Append($"{_schedulingMethodName}({jobArg}, {queryArg}, {dependencyArg}, {systemStateArg}, {hasUserDefinedQueryArg})"); + return stringBuilder.ToString(); + } + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.cs index fc37923..f21e5f9 100644 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.cs +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntityModule.cs @@ -1,70 +1,78 @@ #nullable enable using System; using System.Collections.Generic; -using System.Linq; +using System.Collections.Immutable; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.JobEntity +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +public partial class JobEntityModule : ISystemModule { - public partial class JobEntityModule : ISystemModule + readonly Dictionary> _jobEntityInvocationCandidates = new(); + static readonly ImmutableHashSet ScheduleModes = Enum.GetNames(typeof(ScheduleMode)).ToImmutableHashSet(); + + public IEnumerable<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)> Candidates { - Dictionary> m_JobEntityInvocationCandidates = new Dictionary>(); - public IEnumerable<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)> Candidates + get { - get - { - foreach (var kvp in m_JobEntityInvocationCandidates) - foreach (var candidate in kvp.Value) - yield return (candidate.Node, kvp.Key); - } + foreach (var kvp in _jobEntityInvocationCandidates) + foreach (var candidate in kvp.Value) + yield return (candidate.Node, kvp.Key); } - public bool RequiresReferenceToBurst => true; + } + public bool RequiresReferenceToBurst => true; - public void OnReceiveSyntaxNode(SyntaxNode node) + internal readonly struct JobEntityCandidate : ISystemCandidate + { + public MemberAccessExpressionSyntax MemberAccessExpressionSyntax { get; } + public JobEntityCandidate(MemberAccessExpressionSyntax node) => MemberAccessExpressionSyntax = node; + public string CandidateTypeName => "IJobEntity"; + public SyntaxNode Node => MemberAccessExpressionSyntax; + public InvocationExpressionSyntax Invocation => (MemberAccessExpressionSyntax.Parent as InvocationExpressionSyntax)!; + } + + public void OnReceiveSyntaxNode(SyntaxNode node, Dictionary candidateOwnership) + { + if (node is InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax memberAccessExpressionSyntax } + && memberAccessExpressionSyntax.Kind() == SyntaxKind.SimpleMemberAccessExpression + && memberAccessExpressionSyntax.Expression is IdentifierNameSyntax or ObjectCreationExpressionSyntax) { - if (node is InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax memberAccessExpressionSyntax } - && memberAccessExpressionSyntax.Kind() == SyntaxKind.SimpleMemberAccessExpression - && (memberAccessExpressionSyntax.Expression is IdentifierNameSyntax || memberAccessExpressionSyntax.Expression is ObjectCreationExpressionSyntax)) + var schedulingMethodName = memberAccessExpressionSyntax.Name.Identifier.ValueText; + + if (ScheduleModes.Contains(schedulingMethodName)) { - var schedulingMethodName = memberAccessExpressionSyntax.Name.Identifier.ValueText; + var containingType = node.AncestorOfKind(); - if (Enum.GetNames(typeof(ScheduleMode)).Contains(schedulingMethodName)) - { - var containingType = node.AncestorOfKind(); + // Discard if no base type, meaning it can't possible inherit from a System + if (containingType.BaseList == null || containingType.BaseList.Types.Count == 0) + return; - // Discard if no base type, meaning it can't possible inherit from a System - if (containingType.BaseList == null || containingType.BaseList.Types.Count == 0) - return; - m_JobEntityInvocationCandidates.Add(containingType, new JobEntityCandidate(memberAccessExpressionSyntax)); - } + _jobEntityInvocationCandidates.Add(containingType, new JobEntityCandidate(memberAccessExpressionSyntax)); } } + } - readonly struct JobEntityCandidate : ISystemCandidate - { - public JobEntityCandidate(MemberAccessExpressionSyntax node) => MemberAccessExpressionSyntax = node; - public string CandidateTypeName => "IJobEntity"; - public MemberAccessExpressionSyntax MemberAccessExpressionSyntax { get; } - public SyntaxNode Node => MemberAccessExpressionSyntax; - public InvocationExpressionSyntax? Invocation => MemberAccessExpressionSyntax.Parent as InvocationExpressionSyntax; - } + public bool RegisterChangesInSystem(SystemDescription systemDescription) + { + var candidates = _jobEntityInvocationCandidates[systemDescription.SystemTypeSyntax]; + var validCandidates = new List(candidates.Count); - public bool RegisterChangesInSystem(SystemDescription systemDescription) - { - var candidates = m_JobEntityInvocationCandidates[systemDescription.SystemTypeSyntax]; - var validCandidates = new List(candidates.Count); - foreach (var candidate in candidates) - if (KnownJobEntityInfo.TryCreate(ref systemDescription, candidate, out var knownJobEntityInfo)) - validCandidates.Add(knownJobEntityInfo); + foreach (var candidate in candidates) + if (JobEntityInstanceInfo.TryCreate(ref systemDescription, candidate, out var knownJobEntityInfo)) + { + validCandidates.Add(knownJobEntityInfo); + systemDescription.CandidateNodes.Add( + key: candidate.Invocation, + value: new CandidateSyntax(CandidateType.IJobEntity, CandidateFlags.None, candidate.Invocation)); + } - if (validCandidates.Count > 0) - systemDescription.Rewriters.Add(new Rewriter(ref systemDescription, validCandidates)); + if (validCandidates.Count > 0) + systemDescription.SyntaxWalkers.Add(Module.IJobEntity, new IjeSchedulingSyntaxWalker(ref systemDescription, validCandidates)); - return validCandidates.Count > 0; - } + return validCandidates.Count > 0; } } diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntitySyntaxReceiver.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntitySyntaxReceiver.cs index d3b16ae..59a8df4 100644 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntitySyntaxReceiver.cs +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/JobEntitySyntaxReceiver.cs @@ -4,53 +4,53 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +public static class JobEntitySyntaxFinder { - public static class JobEntitySyntaxFinder + public static bool IsSyntaxTargetForGeneration(SyntaxNode syntaxNode, CancellationToken cancellationToken) { - public static bool IsSyntaxTargetForGeneration(SyntaxNode syntaxNode, CancellationToken cancellationToken) - { - cancellationToken.ThrowIfCancellationRequested(); + cancellationToken.ThrowIfCancellationRequested(); + + // Is Struct + if (syntaxNode is not StructDeclarationSyntax structDeclarationSyntax) + return false; - // Is Struct - if (syntaxNode is StructDeclarationSyntax structDeclarationSyntax) + // Has Base List + if (structDeclarationSyntax.BaseList == null) + return false; + + // Has IJobEntity identifier + var hasIJobEntityIdentifier = false; + foreach (var baseType in structDeclarationSyntax.BaseList.Types) + + if (baseType.Type is IdentifierNameSyntax { Identifier.ValueText: "IJobEntity" }) { - // Has Base List - if (structDeclarationSyntax.BaseList == null) - return false; - - // Has IJobEntity identifier - var hasIJobEntityIdentifier = false; - foreach (var baseType in structDeclarationSyntax.BaseList.Types) - if (baseType.Type is IdentifierNameSyntax { Identifier: { ValueText: "IJobEntity" } }) - { - hasIJobEntityIdentifier = true; - break; - } - if (!hasIJobEntityIdentifier) - return false; - - // Has Partial keyword - var hasPartial = false; - foreach (var m in structDeclarationSyntax.Modifiers) - if (m.IsKind(SyntaxKind.PartialKeyword)) - { - hasPartial = true; - break; - } - - return hasPartial; + hasIJobEntityIdentifier = true; + break; } + + if (!hasIJobEntityIdentifier) return false; - } - - public static StructDeclarationSyntax GetSemanticTargetForGeneration(GeneratorSyntaxContext ctx, CancellationToken cancellationToken) - { - var structDeclarationSyntax = (StructDeclarationSyntax)ctx.Node; - foreach (var baseTypeSyntax in structDeclarationSyntax.BaseList!.Types) - if (ctx.SemanticModel.GetTypeInfo(baseTypeSyntax.Type).Type.ToFullName() == "global::Unity.Entities.IJobEntity") - return structDeclarationSyntax; - return null; - } + + // Has Partial keyword + var hasPartial = false; + foreach (var m in structDeclarationSyntax.Modifiers) + if (m.IsKind(SyntaxKind.PartialKeyword)) + { + hasPartial = true; + break; + } + + return hasPartial; + } + + public static StructDeclarationSyntax GetSemanticTargetForGeneration(GeneratorSyntaxContext ctx, CancellationToken cancellationToken) + { + var structDeclarationSyntax = (StructDeclarationSyntax)ctx.Node; + foreach (var baseTypeSyntax in structDeclarationSyntax.BaseList!.Types) + if (ctx.SemanticModel.GetTypeInfo(baseTypeSyntax.Type).Type.ToFullName() == "global::Unity.Entities.IJobEntity") + return structDeclarationSyntax; + return null; } } diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/RefWrapperType.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/RefWrapperType.cs deleted file mode 100644 index 613c51a..0000000 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/RefWrapperType.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace Unity.Entities.SourceGen.SystemGenerator.Common -{ - public enum RefWrapperType - { - None, - RefRO, - RefRW, - EnabledRefRO, - EnabledRefRW - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/ScheduleMode.cs b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/ScheduleMode.cs index 2f32ac7..f7e2dc1 100644 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/ScheduleMode.cs +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/ScheduleMode.cs @@ -2,49 +2,50 @@ using System; using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Unity.Entities.SourceGen.JobEntity +namespace Unity.Entities.SourceGen.JobEntityGenerator; + +enum ScheduleMode { - enum ScheduleMode - { - Run, - RunByRef, - Schedule, - ScheduleByRef, - ScheduleParallel, - ScheduleParallelByRef, - } - - static class ScheduleModeHelpers { - internal static string GetScheduleMethodWithArguments(this ScheduleMode scheduleMode) => - scheduleMode switch - { - ScheduleMode.Run => "Run(ref job, query)", - ScheduleMode.RunByRef => "Run(ref job, query)", - - ScheduleMode.Schedule => "Schedule(ref job, query, dependency)", - ScheduleMode.ScheduleByRef => "Schedule(ref job, query, dependency)", - - ScheduleMode.ScheduleParallel => "ScheduleParallel(ref job, query, dependency)", - ScheduleMode.ScheduleParallelByRef => "ScheduleParallel(ref job, query, dependency)", - _ => throw new ArgumentOutOfRangeException() - }; - - // scheduleType 0:Run, 1:Schedule, 2:ScheduleParallel - internal static int GetScheduleTypeAsNumber(this ScheduleMode scheduleMode) - => (int) scheduleMode / 2; - - internal static ScheduleMode GetScheduleModeFromNameOfMemberAccess(MemberAccessExpressionSyntax memberAccessExpressionSyntax) - => memberAccessExpressionSyntax.Name.Identifier.ValueText switch - { - "Run" => ScheduleMode.Run, - "RunByRef" => ScheduleMode.RunByRef, - "Schedule" => ScheduleMode.Schedule, - "ScheduleByRef" => ScheduleMode.ScheduleByRef, - "ScheduleParallel" => ScheduleMode.ScheduleParallel, - "ScheduleParallelByRef" => ScheduleMode.ScheduleParallelByRef, - _ => throw new ArgumentOutOfRangeException() - }; - public static bool IsRun(this ScheduleMode mode) => mode == ScheduleMode.Run || mode == ScheduleMode.RunByRef; - public static bool IsByRef(this ScheduleMode mode) => mode == ScheduleMode.RunByRef || mode == ScheduleMode.ScheduleByRef || mode == ScheduleMode.ScheduleParallelByRef; - } + Run, + RunByRef, + Schedule, + ScheduleByRef, + ScheduleParallel, + ScheduleParallelByRef, +} + +static class ScheduleModeHelpers +{ + internal static string GetScheduleMethodWithArguments(this ScheduleMode scheduleMode) => + scheduleMode switch + { + ScheduleMode.Run => "Run(ref job, query)", + ScheduleMode.RunByRef => "Run(ref job, query)", + + ScheduleMode.Schedule => "Schedule(ref job, query, dependency)", + ScheduleMode.ScheduleByRef => "Schedule(ref job, query, dependency)", + + ScheduleMode.ScheduleParallel => "ScheduleParallel(ref job, query, dependency)", + ScheduleMode.ScheduleParallelByRef => "ScheduleParallel(ref job, query, dependency)", + _ => throw new ArgumentOutOfRangeException() + }; + + // scheduleType 0:Run, 1:Schedule, 2:ScheduleParallel + internal static int GetScheduleTypeAsNumber(this ScheduleMode scheduleMode) + => (int) scheduleMode / 2; + + internal static ScheduleMode GetScheduleModeFromNameOfMemberAccess(MemberAccessExpressionSyntax memberAccessExpressionSyntax) + => memberAccessExpressionSyntax.Name.Identifier.ValueText switch + { + "Run" => ScheduleMode.Run, + "RunByRef" => ScheduleMode.RunByRef, + "Schedule" => ScheduleMode.Schedule, + "ScheduleByRef" => ScheduleMode.ScheduleByRef, + "ScheduleParallel" => ScheduleMode.ScheduleParallel, + "ScheduleParallelByRef" => ScheduleMode.ScheduleParallelByRef, + _ => throw new ArgumentOutOfRangeException() + }; + + public static bool IsRun(this ScheduleMode mode) => mode == ScheduleMode.Run || mode == ScheduleMode.RunByRef; + public static bool IsByRef(this ScheduleMode mode) => mode == ScheduleMode.RunByRef || mode == ScheduleMode.ScheduleByRef || mode == ScheduleMode.ScheduleParallelByRef; } diff --git a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/Unity.Entities.SourceGen.JobEntityGenerator.csproj b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/Unity.Entities.SourceGen.JobEntityGenerator.csproj index b8f1ecd..646d20f 100644 --- a/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/Unity.Entities.SourceGen.JobEntityGenerator.csproj +++ b/Unity.Entities/SourceGenerators/Source~/JobEntityGenerator/Unity.Entities.SourceGen.JobEntityGenerator.csproj @@ -4,7 +4,9 @@ RS2007 true netstandard2.0 - 8.0 + latest + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Burst.Mock/Unity.Burst.Mock.csproj b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Burst.Mock/Unity.Burst.Mock.csproj index 44ab994..b494eda 100644 --- a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Burst.Mock/Unity.Burst.Mock.csproj +++ b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Burst.Mock/Unity.Burst.Mock.csproj @@ -7,5 +7,7 @@ disable Unity.Burst Unity.Burst + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Collections.Mock/Unity.Collections.Mock.csproj b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Collections.Mock/Unity.Collections.Mock.csproj index 67f6481..59c7efb 100644 --- a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Collections.Mock/Unity.Collections.Mock.csproj +++ b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Collections.Mock/Unity.Collections.Mock.csproj @@ -7,5 +7,7 @@ disable Unity.Collections Unity.Collections + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Hybrid.Mock/Unity.Entities.Hybrid.Mock.csproj b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Hybrid.Mock/Unity.Entities.Hybrid.Mock.csproj index 15572e6..f88cb54 100644 --- a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Hybrid.Mock/Unity.Entities.Hybrid.Mock.csproj +++ b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Hybrid.Mock/Unity.Entities.Hybrid.Mock.csproj @@ -8,6 +8,8 @@ Unity.Entities.Hybrid Unity.Entities.Hybrid true + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/EntitiesInternalMock.cs b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/EntitiesInternalMock.cs index 9f2e791..0b67b0a 100644 --- a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/EntitiesInternalMock.cs +++ b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/EntitiesInternalMock.cs @@ -1,4 +1,5 @@ using System.Runtime.CompilerServices; +using Unity.Burst.Intrinsics; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Jobs; @@ -7,6 +8,122 @@ namespace Unity.Entities.Internal; public static partial class InternalCompilerInterface { + public interface IAspectLookup where T : IAspect + { + public void Update(ref SystemState state); + public T this[Entity entity] { get; } + } + + public static EntityStorageInfoLookup GetEntityStorageInfoLookup( + ref EntityStorageInfoLookup entityStorageInfoLookup, ref SystemState state) + => default; + + public static bool DoesEntityExist(ref EntityStorageInfoLookup entityStorageInfoLookup, ref SystemState state, + Entity entity) + => default; + + public static EntityTypeHandle GetEntityTypeHandle(ref EntityTypeHandle entityTypeHandle, ref SystemState state) + => default; + + public static ComponentLookup GetComponentLookup(ref ComponentLookup componentLookup, + ref SystemState state) where T : unmanaged, IComponentData + => default; + + public static BufferLookup GetBufferLookup(ref BufferLookup bufferLookup, ref SystemState state) + where T : unmanaged, IBufferElementData + => default; + + public static RefRO GetComponentROAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + Entity entity) where T : unmanaged, IComponentData + => default; + + public static RefRW GetComponentRWAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + Entity entity) where T : unmanaged, IComponentData + => default; + + public static T GetComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, Entity entity) + where T : unmanaged, IComponentData + => default; + + public static RefRW GetComponentRWAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + SystemHandle systemHandle) where T : unmanaged, IComponentData + => default; + + public static T GetComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + SystemHandle systemHandle) where T : unmanaged, IComponentData + => default; + public static void SetComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, T component, + Entity entity) where T : unmanaged, IComponentData + { + } + + public static void SetComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, T component, + SystemHandle systemHandle) where T : unmanaged, IComponentData + { + } + + public static bool HasComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, Entity entity) + where T : unmanaged, IComponentData + => default; + + public static bool HasComponentAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + SystemHandle systemHandle) where T : unmanaged, IComponentData + => default; + + public static bool IsComponentEnabledAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + Entity entity) where T : unmanaged, IComponentData + => default; + + public static bool IsComponentEnabledAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + SystemHandle systemHandle) where T : unmanaged, IComponentData + => default; + + public static void SetComponentEnabledAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + Entity entity, bool value) where T : unmanaged, IComponentData + { + } + + public static void SetComponentEnabledAfterCompletingDependency(ref ComponentLookup componentLookup, ref SystemState state, + SystemHandle systemHandle, bool value) where T : unmanaged, IComponentData + { + } + + public static DynamicBuffer GetBufferAfterCompletingDependency(ref BufferLookup bufferLookup, ref SystemState state, + Entity entity) where T : unmanaged, IBufferElementData + => default; + + public static bool HasBufferAfterCompletingDependency(ref BufferLookup bufferLookup, ref SystemState state, Entity entity) + where T : unmanaged, IBufferElementData + => default; + + public static bool IsBufferEnabledAfterCompletingDependency(ref BufferLookup bufferLookup, ref SystemState state, Entity entity) + where T : unmanaged, IBufferElementData + => default; + + public static void SetBufferEnabledAfterCompletingDependency(ref BufferLookup bufferLookup, ref SystemState state, bool value, + Entity entity) where T : unmanaged, IBufferElementData + { + } + + public static T GetAspectAfterCompletingDependency(ref TLookup aspectLookup, ref SystemState state, bool isAspectReadOnly, + Entity entity) + where TLookup : struct, IAspectLookup + where T : struct, IAspect, IAspectCreate + => default; + + public static ComponentTypeHandle GetComponentTypeHandle(ref ComponentTypeHandle componentTypeHandle, + ref SystemState state) where T : unmanaged, IComponentData + => default; + + public static BufferTypeHandle GetBufferTypeHandle(ref BufferTypeHandle bufferTypeHandle, + ref SystemState state) where T : unmanaged, IBufferElementData + => default; + + public static SharedComponentTypeHandle GetSharedComponentTypeHandle( + ref SharedComponentTypeHandle sharedComponentTypeHandle, ref SystemState state) + where T : struct, ISharedComponentData + => default; + public static void MergeWith(ref UnsafeList componentTypes, ref UnsafeList addThese) { } @@ -66,6 +183,14 @@ public static unsafe void WriteComponentData(EntityManager manager, Entity en public static unsafe ref T UnsafeGetRefToNativeArrayPtrElement(IntPtr nativeArrayPtr, int elementIndex) where T : unmanaged => throw new Exception(); public static unsafe IntPtr UnsafeGetSystemStateIntPtr(ref SystemState state) => default; public static unsafe ref SystemState UnsafeGetSystemStateRef(IntPtr statePtr) => ref *(SystemState*) statePtr; + public static bool UnsafeTryGetNextEnabledBitRange(v128 mask, int firstIndexToCheck, out int nextRangeBegin, + out int nextRangeEnd) + { + nextRangeBegin = default; + nextRangeEnd = default; + return default; + } + public static class JobChunkInterface { public static void RunWithoutJobsInternal(ref T jobData, ref EntityQuery query) where T : struct, IJobChunk {} @@ -113,12 +238,12 @@ public void Dispose() {} public bool MoveNextColdLoop(out ArchetypeChunk chunk) => true; [MethodImpl(MethodImplOptions.AggressiveInlining)] - public bool MoveNextEntityRange(out bool movedToNewChunk, out ArchetypeChunk chunk, out int entityStartIndex, out int entityCount) + public bool MoveNextEntityRange(out bool movedToNewChunk, out ArchetypeChunk chunk, out int entityStartIndex, out int entityEndIndex) { movedToNewChunk = false; chunk = default; entityStartIndex = -1; - entityCount = -1; + entityEndIndex = -1; return false; } } diff --git a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/EntitiesMock.cs b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/EntitiesMock.cs index 41c789b..d952dba 100644 --- a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/EntitiesMock.cs +++ b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/EntitiesMock.cs @@ -9,6 +9,8 @@ namespace Unity.Entities { + public struct SystemHandle{} + public class EntitiesMock { } public struct Entity : IQueryTypeParameter { } @@ -34,7 +36,7 @@ public void Reset() public EntityQueryBuilder WithDisabled() => this; public EntityQueryBuilder WithAbsent() => this; public EntityQueryBuilder WithAspect() => this; - public EntityQueryBuilder WithOptions() => this; + public EntityQueryBuilder WithOptions(EntityQueryOptions options) => this; public EntityQueryBuilder AddAdditionalQuery() => this; public EntityQuery Build(ref SystemState systemState) => default; } @@ -362,9 +364,19 @@ public interface IEnableableComponent { } public interface IBufferElementData { } - public struct DynamicBuffer where T : unmanaged + public struct DynamicBuffer : IQueryTypeParameter where T : unmanaged { - public ref T this[int i] => throw new NotImplementedException(); + public T this[int i] + { + get + { + return default; + } + set + { + value = default; + } + } } public struct SystemState @@ -389,9 +401,18 @@ public void CompleteDependency(){} public struct ComponentType { + public enum AccessMode + { + ReadWrite, + ReadOnly, + Exclude + } public static unsafe ComponentType[] Combine(params ComponentType[][] componentTypes) => default; public static ComponentType ReadWrite() => default; public static ComponentType ReadOnly() => default; + public ComponentType(Type type, AccessMode accessModeType = AccessMode.ReadWrite) + { + } } public struct EntityQuery @@ -402,6 +423,10 @@ public struct EntityQuery public T GetSingleton() where T : unmanaged, IComponentData => default; public void SetChangedVersionFilter(ComponentType componentType) {} public void SetChangedVersionFilter(ComponentType[] componentType) {} + public void SetSharedComponentFilter(T sharedComponent) where T : unmanaged, ISharedComponentData + { + } + public void ResetFilter() {} public bool IsEmptyIgnoreFilter => default; public NativeArray CalculateBaseEntityIndexArrayAsync(AllocatorHandle allocator, JobHandle additionalInputDep, out JobHandle outJobHandle) => default; @@ -478,6 +503,8 @@ public static class ManagedAPI } public static bool HasSingleton() where T : unmanaged => throw new Exception(); + public static bool IsComponentEnabled(Entity entity) where T : unmanaged, IComponentData, IEnableableComponent => throw new Exception(); + public static void SetComponentEnabled(Entity entity, bool value) where T : unmanaged, IComponentData, IEnableableComponent => throw new Exception(); } public struct QueryEnumerable : IEnumerable where T : struct @@ -487,8 +514,8 @@ public struct QueryEnumerable : IEnumerable where T : struct public QueryEnumerable WithNone() => throw ThrowCodeGenException(); public QueryEnumerable WithDisabled() => throw ThrowCodeGenException(); public QueryEnumerable WithAbsent() => throw ThrowCodeGenException(); - public QueryEnumerable WithSharedComponentFilter() => throw ThrowCodeGenException(); - public QueryEnumerable WithSharedComponentFilter() => throw ThrowCodeGenException(); + public QueryEnumerable WithSharedComponentFilter(T1 sharedComp) => throw ThrowCodeGenException(); + public QueryEnumerable WithSharedComponentFilter(T1 sharedComp1, T2 sharedComp2) => throw ThrowCodeGenException(); public QueryEnumerable WithChangeFilter() => throw ThrowCodeGenException(); public QueryEnumerable WithChangeFilter() => throw ThrowCodeGenException(); public QueryEnumerable WithFilter(NativeArray entities) => throw ThrowCodeGenException(); @@ -522,6 +549,8 @@ public interface IAspectCreate : IQueryTypeParameter where T : IAspect { T CreateAspect(Entity entity, ref SystemState system); void AddComponentRequirementsTo(ref UnsafeList all); + void CompleteDependencyBeforeRO(ref SystemState state); + void CompleteDependencyBeforeRW(ref SystemState state); } public interface ISystemCompilerGenerated @@ -612,16 +641,6 @@ public void AddComponent(EntityQuery query) {} public void AddComponentData(Entity entity, T componentData) where T : class, IComponentData {} } - public static class EnabledBitUtility - { - public static bool TryGetNextRange(v128 mask, int firstIndexToCheck, out int currentRangeBegin, out int currentRangeEnd) - { - currentRangeBegin = 0; - currentRangeEnd = 0; - return true; - } - } - namespace Serialization { public struct UntypedWeakReferenceId {} diff --git a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/TestComponents.cs b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/TestComponents.cs index ac9180e..dfaabb2 100644 --- a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/TestComponents.cs +++ b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/TestComponents.cs @@ -4,6 +4,7 @@ using System.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Entities; +using Unity.Entities.Internal; using Unity.Entities.Serialization; namespace Unity.Entities.Tests @@ -18,7 +19,7 @@ public struct EcsIntElementEnableable : IBufferElementData, IEnableableComponent public int Value; } - public struct GenericComponentData : IComponentData where T : unmanaged, IComponentData + public struct GenericComponentData : IComponentData where T : unmanaged { } @@ -65,6 +66,15 @@ public struct EcsTestData5 : IComponentData public struct EcsIntElement : IBufferElementData { public int Value; + public static implicit operator int(EcsIntElement a) => a.Value; + public static implicit operator EcsIntElement(int val) => new() {Value = val}; + } + + public struct EcsFloatElement : IBufferElementData + { + public float Value; + public static implicit operator float(EcsFloatElement a) => a.Value; + public static implicit operator EcsFloatElement(float val) => new() {Value = val}; } public struct EcsTestSharedComp : ISharedComponentData @@ -77,6 +87,16 @@ public EcsTestSharedComp(int inValue) } } + public struct EcsTestSharedCompA : ISharedComponentData + { + public int value; + } + + public struct EcsTestSharedCompB : ISharedComponentData + { + public int value; + } + public class EcsTestManagedComponent : IComponentData { public string value; @@ -95,7 +115,7 @@ public EcsTestAspect CreateAspect(Entity entity, ref SystemState system) => public void AddComponentRequirementsTo(ref UnsafeList all) => throw new NotImplementedException(); - public struct Lookup + public struct Lookup : InternalCompilerInterface.IAspectLookup { public Lookup(ref global::Unity.Entities.SystemState state) => throw new NotImplementedException(); @@ -134,8 +154,8 @@ public void Reset() {} IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); } - public static void CompleteDependencyBeforeRO(ref SystemState state) { } - public static void CompleteDependencyBeforeRW(ref SystemState state) { } + public void CompleteDependencyBeforeRO(ref SystemState state) { } + public void CompleteDependencyBeforeRW(ref SystemState state) { } } public struct EcsTestDataEntity : IComponentData diff --git a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/Unity.Entities.Mock.csproj b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/Unity.Entities.Mock.csproj index 1acf04a..90d399f 100644 --- a/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/Unity.Entities.Mock.csproj +++ b/Unity.Entities/SourceGenerators/Source~/Mock/Unity.Entities.Mock/Unity.Entities.Mock.csproj @@ -8,6 +8,8 @@ Unity.Entities Unity.Entities true + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Archetype.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Archetype.cs index 3848fbf..b1f4090 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Archetype.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Archetype.cs @@ -1,63 +1,69 @@ using System; using System.Collections.Generic; using System.Linq; +using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public readonly struct Archetype : IEquatable { - public readonly struct Archetype : IEquatable - { - public readonly EntityQueryOptions Options; + public readonly EntityQueryOptions Options; - // Aspect types may be present in the All collection - public readonly IReadOnlyCollection All; - public readonly IReadOnlyCollection Any; - public readonly IReadOnlyCollection None; - public readonly IReadOnlyCollection Disabled; - public readonly IReadOnlyCollection Absent; + // Aspect types may be present in the All collection + public readonly IReadOnlyCollection All; + public readonly IReadOnlyCollection Any; + public readonly IReadOnlyCollection None; + public readonly IReadOnlyCollection Disabled; + public readonly IReadOnlyCollection Absent; + public readonly IReadOnlyCollection Present; - public Archetype( - IReadOnlyCollection all, - IReadOnlyCollection any, - IReadOnlyCollection none, - IReadOnlyCollection disabled, - IReadOnlyCollection absent, - EntityQueryOptions options = default) - { - All = all; - Any = any; - None = none; - Disabled = disabled; - Absent = absent; - Options = options; - } + public Archetype( + IReadOnlyCollection all, + IReadOnlyCollection any, + IReadOnlyCollection none, + IReadOnlyCollection disabled, + IReadOnlyCollection absent, + IReadOnlyCollection present, + EntityQueryOptions options = default) + { + All = all; + Any = any; + None = none; + Disabled = disabled; + Absent = absent; + Present = present; + Options = options; + } - public bool Equals(Archetype other) => - Options == other.Options - && All.SequenceEqual(other.All) - && Any.SequenceEqual(other.Any) - && None.SequenceEqual(other.None) - && Disabled.SequenceEqual(other.Disabled) - && Absent.SequenceEqual(other.Absent); - public override int GetHashCode() + public bool Equals(Archetype other) => + Options == other.Options + && All.SequenceEqual(other.All) + && Any.SequenceEqual(other.Any) + && None.SequenceEqual(other.None) + && Disabled.SequenceEqual(other.Disabled) + && Present.SequenceEqual(other.Present) + && Absent.SequenceEqual(other.Absent); + public override int GetHashCode() + { + unchecked { - unchecked - { - int hash = 19; + int hash = 19; - foreach (var all in All) - hash = hash * 31 + all.GetHashCode(); - foreach (var any in Any) - hash = hash * 31 + any.GetHashCode(); - foreach (var none in None) - hash = hash * 31 + none.GetHashCode(); - foreach (var disabled in Disabled) - hash = hash * 31 + disabled.GetHashCode(); - foreach (var absent in Absent) - hash = hash * 31 + absent.GetHashCode(); - hash = hash * 31 + ((int)Options).GetHashCode(); + foreach (var all in All) + hash = hash * 31 + all.GetHashCode(); + foreach (var any in Any) + hash = hash * 31 + any.GetHashCode(); + foreach (var none in None) + hash = hash * 31 + none.GetHashCode(); + foreach (var disabled in Disabled) + hash = hash * 31 + disabled.GetHashCode(); + foreach (var absent in Absent) + hash = hash * 31 + absent.GetHashCode(); + foreach (var present in Present) + hash = hash * 31 + present.GetHashCode(); + hash = hash * 31 + ((int)Options).GetHashCode(); - return hash; - } + return hash; } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/CorrectionSystemRewriter.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/CorrectionSystemRewriter.cs deleted file mode 100644 index d71de03..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/CorrectionSystemRewriter.cs +++ /dev/null @@ -1,172 +0,0 @@ -using System.Collections.Generic; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.Common; - -namespace Unity.Entities.SourceGen.SystemGenerator.Common -{ - /// - /// The CorrectionSystemReplacer addresses 3 concerns. - /// 1. Adding line directives to every statement (needed as other replacers might put in additional lines, which will offset subsequent nodes. - /// 2. Ensuring valid syntax for block statements who are not in a block e.g. for, foreach etc. (as inserting syntax might otherwise fail). - /// 3. Replacing non-nested nodes (Temporary while LambdaJobs and JobEntity still doesn't use SystemRewriter) - /// - /// - /// Has to be run before other SystemRewriters, - /// as line offsetting will be expentionally harder to get as you keep replacing, - /// this rewriter works on the fact that the System is still yet to be replaced. - /// - public class CorrectionSystemReplacer : SystemRewriter - { - readonly Dictionary m_OriginalToReplacementNodes; - readonly Dictionary m_NodesToReplacements; - public CorrectionSystemReplacer(Dictionary descriptionNonNestedReplacementsInMethods) - { - m_OriginalToReplacementNodes = descriptionNonNestedReplacementsInMethods; - m_NodesToReplacements = new Dictionary(descriptionNonNestedReplacementsInMethods.Count); - } - - public override IEnumerable NodesToTrack => m_OriginalToReplacementNodes.Keys; - - public void SetOffsetLineNumber(int offset) => m_OffsetLineNumber = offset; - - public override SyntaxNode VisitTrackedSystem(SyntaxNode systemRootNode, string originalFilePath) - { - m_OriginalFilePath = originalFilePath; - m_OffsetLineNumber -= systemRootNode.GetLineNumber(); - - foreach (var replacement in m_OriginalToReplacementNodes) - { - // `.GetCurrentNode()` returns the node in the tracked system that corresponds to the original node we want to replace - var correspondingTrackedNode = systemRootNode.GetCurrentNode(replacement.Key); - m_NodesToReplacements[correspondingTrackedNode] = replacement.Value; - } - - return Visit(systemRootNode); - } - - int m_OffsetLineNumber; - - bool m_ReplacedNode; - public override SyntaxNode Visit(SyntaxNode node) - { - if (node == null) - return null; - - var newNode = base.Visit(node); - // If this is a node we want to replace - if (m_NodesToReplacements.TryGetValue(node, out var replacement)) - { - if (replacement == null) - return null; - - // Ensure that all the annotations made by Roslyn for tracking purposes are preserved - newNode = node.CopyAnnotationsTo(replacement); - m_ReplacedNode = true; - } - - if (newNode is MemberDeclarationSyntax memberDeclarationSyntax && m_ReplacedNode) - { - RecordChangedMember(memberDeclarationSyntax); - m_ReplacedNode = false; - } - newNode = newNode is StatementSyntax && !(newNode is BlockSyntax) ? newNode.WithLineTrivia(m_OriginalFilePath, node.GetLineNumber(), m_OffsetLineNumber) : newNode; - return newNode; - } - - public override SyntaxNode VisitForStatement(ForStatementSyntax node) - { - var replacedUpUntilThisNode = base.VisitForStatement(node) as ForStatementSyntax; - - if (!(node.Statement is BlockSyntax) && replacedUpUntilThisNode != null) - if (SyntaxFactory.Block(replacedUpUntilThisNode.Statement) is StatementSyntax block) - replacedUpUntilThisNode = replacedUpUntilThisNode.WithStatement(block); - - return replacedUpUntilThisNode; - } - - public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node) - { - var replacedUpUntilThisNode = base.VisitMethodDeclaration(node) as MethodDeclarationSyntax; - - if (replacedUpUntilThisNode?.ExpressionBody is { } expressionBody) - { - replacedUpUntilThisNode = replacedUpUntilThisNode.WithExpressionBody(null); - replacedUpUntilThisNode = replacedUpUntilThisNode.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.None)); - replacedUpUntilThisNode = replacedUpUntilThisNode.WithBody( - replacedUpUntilThisNode.ReturnType is PredefinedTypeSyntax predefinedTypeSyntax && predefinedTypeSyntax.Keyword.IsKind(SyntaxKind.VoidKeyword) - ? SyntaxFactory.Block(SyntaxFactory.ExpressionStatement(expressionBody.Expression)) - : SyntaxFactory.Block(SyntaxFactory.ReturnStatement(expressionBody.Expression))); - } - - return replacedUpUntilThisNode; - } - - public override SyntaxNode VisitDoStatement(DoStatementSyntax node) - { - var replacedUpUntilThisNode = base.VisitDoStatement(node) as DoStatementSyntax; - - if (!(node.Statement is BlockSyntax) && replacedUpUntilThisNode != null) - if (SyntaxFactory.Block(replacedUpUntilThisNode.Statement) is StatementSyntax block) - replacedUpUntilThisNode = replacedUpUntilThisNode.WithStatement(block); - - return replacedUpUntilThisNode; - } - - public override SyntaxNode VisitWhileStatement(WhileStatementSyntax node) - { - var replacedUpUntilThisNode = base.VisitWhileStatement(node) as WhileStatementSyntax; - - if (!(node.Statement is BlockSyntax) && replacedUpUntilThisNode != null) - if (SyntaxFactory.Block(replacedUpUntilThisNode.Statement) is StatementSyntax block) - replacedUpUntilThisNode = replacedUpUntilThisNode.WithStatement(block); - - return replacedUpUntilThisNode; - } - - public override SyntaxNode VisitForEachVariableStatement(ForEachVariableStatementSyntax node) - { - var replacedUpUntilThisNode = base.VisitForEachVariableStatement(node) as ForEachVariableStatementSyntax; - - if (!(node.Statement is BlockSyntax) && replacedUpUntilThisNode != null) - if (SyntaxFactory.Block(replacedUpUntilThisNode.Statement) is StatementSyntax block) - replacedUpUntilThisNode = replacedUpUntilThisNode.WithStatement(block); - - return replacedUpUntilThisNode; - } - - public override SyntaxNode VisitForEachStatement(ForEachStatementSyntax node) - { - var replacedUpUntilThisNode = base.VisitForEachStatement(node) as ForEachStatementSyntax; - - if (!(node.Statement is BlockSyntax) && replacedUpUntilThisNode != null) - if (SyntaxFactory.Block(replacedUpUntilThisNode.Statement) is StatementSyntax block) - replacedUpUntilThisNode = replacedUpUntilThisNode.WithStatement(block); - - return replacedUpUntilThisNode; - } - - public override SyntaxNode VisitIfStatement(IfStatementSyntax node) - { - var replacedUpUntilThisNode = base.VisitIfStatement(node) as IfStatementSyntax; - - if (!(node.Statement is BlockSyntax) && replacedUpUntilThisNode != null) - if (SyntaxFactory.Block(replacedUpUntilThisNode.Statement) is StatementSyntax block) - replacedUpUntilThisNode = replacedUpUntilThisNode.WithStatement(block); - - return replacedUpUntilThisNode; - } - - public override SyntaxNode VisitElseClause(ElseClauseSyntax node) - { - var replacedUpUntilThisNode = base.VisitElseClause(node) as ElseClauseSyntax; - - if (!(node.Statement is BlockSyntax) && replacedUpUntilThisNode != null) - if (SyntaxFactory.Block(replacedUpUntilThisNode.Statement) is StatementSyntax block) - replacedUpUntilThisNode = replacedUpUntilThisNode.WithStatement(block); - - return replacedUpUntilThisNode; - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/EntitiesSourceFactory.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/EntitiesSourceFactory.cs deleted file mode 100644 index d64246f..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/EntitiesSourceFactory.cs +++ /dev/null @@ -1,36 +0,0 @@ -using System.Collections.Generic; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.Common; - -namespace Unity.Entities.SourceGen.SystemGenerator.Common -{ - public static class EntitiesSourceFactory - { - internal static MethodDeclarationSyntax - OnCreateForCompilerMethod(IEnumerable additionalSyntax, bool isInISystem) - { - - var onCreateMethod = - isInISystem - ? $@"public void OnCreateForCompiler(ref SystemState state) - {{ - __AssignQueries(ref state); - __TypeHandle.__AssignHandles(ref state); - {additionalSyntax.SeparateByNewLine()} - }}" - : $@"protected override void OnCreateForCompiler() - {{ - base.OnCreateForCompiler(); - __AssignQueries(ref this.CheckedStateRef); - __TypeHandle.__AssignHandles(ref this.CheckedStateRef); - {additionalSyntax.SeparateByNewLine()} - }}"; - - return (MethodDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(onCreateMethod); - } - - internal static MethodDeclarationSyntax OnCreateForCompilerStub() - => (MethodDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration("public void OnCreateForCompiler(ref SystemState state){}"); - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Enums.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Enums.cs index 77a6237..e627b03 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Enums.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Enums.cs @@ -1,33 +1,8 @@ -using System; -using Microsoft.CodeAnalysis.CSharp.Syntax; +namespace Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +public enum SystemType { - [Flags] - public enum EntityQueryOptions - { - Default = 0, - IncludePrefab = 1, - IncludeDisabledEntities = 2, - FilterWriteGroup = 4, - IgnoreComponentEnabledState = 8, - IncludeSystems = 16, - } - - public static class EntityQueryOptionsExtensions - { - public static EntityQueryOptions GetEntityQueryOptions(this MemberAccessExpressionSyntax expression) - { - return expression.Name.Identifier.ValueText switch - { - "Default" => EntityQueryOptions.Default, - "IncludePrefab" => EntityQueryOptions.IncludePrefab, - "IncludeDisabledEntities" => EntityQueryOptions.IncludeDisabledEntities, - "FilterWriteGroup" => EntityQueryOptions.FilterWriteGroup, - "IgnoreComponentEnabledState" => EntityQueryOptions.IgnoreComponentEnabledState, - "IncludeSystems" => EntityQueryOptions.IncludeSystems, - _ => throw new ArgumentOutOfRangeException() - }; - } - } + Unknown, + SystemBase, + ISystem } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/HandlesDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/HandlesDescription.cs deleted file mode 100644 index 2929202..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/HandlesDescription.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.Common; - -namespace Unity.Entities.SourceGen.SystemGenerator.Common -{ - public readonly struct HandlesDescription - { - public readonly HashSet NonQueryFields; - public readonly Dictionary QueryFieldsToFieldNames; - public readonly int UniqueId; - HandlesDescription(HashSet nonQueryFields, Dictionary queryFieldsToFieldNames, SyntaxNode owningType) - { - NonQueryFields = nonQueryFields; - QueryFieldsToFieldNames = queryFieldsToFieldNames; - var systemLineSpan = owningType.GetLocation().GetLineSpan(); - UniqueId = (systemLineSpan.Span.Start.Line + SourceGenHelpers.GetStableHashCode(Path.GetFileName(systemLineSpan.Path))) & 0x7fffffff; - } - - public static HandlesDescription Create(TypeDeclarationSyntax owningType) - => new HandlesDescription( - new HashSet(), - new Dictionary(), owningType); - - public string GetOrCreateQueryField(IQueryFieldDescription queryFieldDescription, string generatedName = null) - { - if (QueryFieldsToFieldNames.TryGetValue(queryFieldDescription, out string matchingFieldName)) - return matchingFieldName; - generatedName ??= $"__query_{UniqueId}_{QueryFieldsToFieldNames.Count}"; - QueryFieldsToFieldNames.Add(queryFieldDescription, generatedName); - return generatedName; - } - - // We cannot call `GetOrCreateTypeHandleField(ITypeSymbol typeSymbol, bool isReadOnly)` when creating type handle - // fields for source-generated types, because there aren't any type symbols available yet. - public string GetOrCreateSourceGeneratedTypeHandleField(string containerTypeFullName) - { - var description = new ContainerTypeHandleFieldDescription(containerTypeFullName); - NonQueryFields.Add(description); - - return description.GeneratedFieldName; - } - - public string GetOrCreateAspectLookup(ITypeSymbol entityTypeLookup, bool isReadOnly) - { - var entityTypeLookupField = new AspectLookupFieldDescription(entityTypeLookup, isReadOnly); - NonQueryFields.Add(entityTypeLookupField); - - return entityTypeLookupField.GeneratedFieldName; - } - - public string GetOrCreateEntityTypeHandleField() - { - var entityTypeHandleFieldDescription = new EntityTypeHandleFieldDescription(); - NonQueryFields.Add(entityTypeHandleFieldDescription); - - return entityTypeHandleFieldDescription.GeneratedFieldName; - } - - public string GetOrCreateTypeHandleField(ITypeSymbol typeSymbol, bool isReadOnly) - { - var typeHandleFieldDescription = new TypeHandleFieldDescription(typeSymbol, isReadOnly); - NonQueryFields.Add(typeHandleFieldDescription); - - return typeHandleFieldDescription.GeneratedFieldName; - } - - public string GetOrCreateComponentLookupField(ITypeSymbol typeSymbol, bool isReadOnly) - { - var lookupField = new ComponentLookupFieldDescription(typeSymbol, isReadOnly); - NonQueryFields.Add(lookupField); - - return lookupField.GeneratedFieldName; - } - - public string GetOrCreateJobEntityHandle(ITypeSymbol typeSymbol, bool assignDefaultQuery) - { - var lookupField = new JobEntityQueryAndHandleDescription(typeSymbol, assignDefaultQuery); - NonQueryFields.Add(lookupField); - return lookupField.GeneratedFieldName; - } - - public string GetOrCreateBufferLookupField(ITypeSymbol typeSymbol, bool isReadOnly) - { - var bufferLookupField = new BufferLookupFieldDescription(typeSymbol, isReadOnly); - NonQueryFields.Add(bufferLookupField); - - return bufferLookupField.GeneratedFieldName; - } - - public string GetOrCreateEntityStorageInfoLookupField() - { - var storageLookupField = new EntityStorageInfoLookupFieldDescription(); - NonQueryFields.Add(storageLookupField); - - return storageLookupField.GeneratedFieldName; - } - - // Expects the first handle description to be the one where the TypeHandle is generated. - // By the point this is called all HandleDescriptions should have been filled out! - public static string GetTypeHandleForInitialPartial(bool shouldHaveUpdate, bool fieldsArePublic, string additionalSyntax, params HandlesDescription[] handleDescriptions) - { - if (handleDescriptions.Length == 0) - throw new InvalidOperationException("Didn't pass in any handle Descriptions"); - - var allQueryFieldsToGenerate = handleDescriptions.SelectMany(d=> d.QueryFieldsToFieldNames).ToArray(); - - // This handles the aliasing of lookups and typehandles - var nonQueryFields = handleDescriptions[0].NonQueryFields; - for (var i = 1; i < handleDescriptions.Length; i++) - { - var desc = handleDescriptions[i]; - foreach (var nonQueryField in desc.NonQueryFields) - nonQueryFields.Add(nonQueryField); - } - - bool useEntityQueryBuilder = allQueryFieldsToGenerate.Any(kvp => kvp.Key is MultipleArchetypeQueryFieldDescription); - - var nonQueryFieldsStringBuilder = new StringBuilder(nonQueryFields.Count*2); - foreach (var nonQueryField in nonQueryFields) - { - nonQueryFieldsStringBuilder.Append(" "); - nonQueryFieldsStringBuilder.AppendLine(nonQueryField.GetFieldDeclaration(true)); - } - - var queryFieldsStringBuilder = new StringBuilder(allQueryFieldsToGenerate.Length*2); - foreach (var kvp in allQueryFieldsToGenerate) - { - queryFieldsStringBuilder.Append(" "); - queryFieldsStringBuilder.AppendLine(kvp.Key.GetFieldDeclaration(kvp.Value, fieldsArePublic)); - } - - var assignQueries = $@"[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - void __AssignQueries(ref global::Unity.Entities.SystemState state) - {{ - {"var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp);".EmitIfTrue(useEntityQueryBuilder)} - {allQueryFieldsToGenerate.Select(kvp => kvp.Key.EntityQueryFieldAssignment(kvp.Value)).SeparateByNewLine()} - {"entityQueryBuilder.Dispose();".EmitIfTrue(useEntityQueryBuilder)} - }}"; - - var assignHandles = $@"[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void __AssignHandles(ref global::Unity.Entities.SystemState state) - {{ - {nonQueryFields.Select(field => field.GetFieldAssignment()).SeparateByNewLine()} - }}"; - - - var update = shouldHaveUpdate ? $@"public void Update(ref global::Unity.Entities.SystemState state) {{ - {string.Join("\n ", nonQueryFields.Select(f=> $"{f.GeneratedFieldName}.Update(ref state);"))} - }}" : ""; - - - - // Create structures. - return @$"/// Used internally by the compiler, we won't promise this exists in the future -public struct InternalCompilerQueryAndHandleData -{{ - {"public ".EmitIfTrue(fieldsArePublic)}TypeHandle __TypeHandle; -{queryFieldsStringBuilder} - - {"public ".EmitIfTrue(fieldsArePublic)}struct TypeHandle - {{ -{nonQueryFieldsStringBuilder} - {assignHandles} - {update} - }} - - {assignQueries} - {additionalSyntax} -}}"; - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/AspectLookupFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/AspectLookupFieldDescription.cs index c011c6b..e8a0f72 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/AspectLookupFieldDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/AspectLookupFieldDescription.cs @@ -1,38 +1,46 @@ using System; +using System.CodeDom.Compiler; using Microsoft.CodeAnalysis; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public readonly struct AspectLookupFieldDescription : IEquatable, IMemberDescription { - public readonly struct AspectLookupFieldDescription : IEquatable, INonQueryFieldDescription + ITypeSymbol TypeSymbol { get; } + bool IsReadOnly { get; } + public string GeneratedFieldName { get; } + + public void AppendMemberDeclaration(IndentedTextWriter w, bool forcePublic = false) { - ITypeSymbol TypeSymbol { get; } - bool IsReadOnly { get; } - public string GeneratedFieldName { get; } + if (IsReadOnly) + w.Write("[global::Unity.Collections.ReadOnly] "); + if (forcePublic) + w.Write("public "); + w.Write($"{TypeSymbol.ToFullName()}.Lookup {GeneratedFieldName};"); + w.WriteLine(); + } - public string GetFieldDeclaration(bool forcePublic = false) => - $"{"[global::Unity.Collections.ReadOnly] ".EmitIfTrue(IsReadOnly)} {(forcePublic ? "public" : "")} {TypeSymbol.ToFullName()}.Lookup {GeneratedFieldName};"; - public string GetFieldAssignment() => - $@"{GeneratedFieldName} = new {TypeSymbol.ToFullName()}.Lookup(ref state);"; + public string GetMemberAssignment() => + $@"{GeneratedFieldName} = new {TypeSymbol.ToFullName()}.Lookup(ref state);"; - public AspectLookupFieldDescription(ITypeSymbol typeSymbol, bool isReadOnly) - { - TypeSymbol = typeSymbol; - IsReadOnly = isReadOnly; + public AspectLookupFieldDescription(ITypeSymbol typeSymbol, bool isReadOnly) + { + TypeSymbol = typeSymbol; + IsReadOnly = isReadOnly; - GeneratedFieldName = $"__{TypeSymbol.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_AspectLookup"; - } + GeneratedFieldName = $"__{TypeSymbol.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_AspectLookup"; + } - public bool Equals(AspectLookupFieldDescription other) => - SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) && IsReadOnly == other.IsReadOnly; + public bool Equals(AspectLookupFieldDescription other) => + SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) && IsReadOnly == other.IsReadOnly; - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - return ((TypeSymbol != null ? - SymbolEqualityComparer.Default.GetHashCode(TypeSymbol) : 0) * 397) ^ IsReadOnly.GetHashCode(); - } + return ((TypeSymbol != null ? + SymbolEqualityComparer.Default.GetHashCode(TypeSymbol) : 0) * 397) ^ IsReadOnly.GetHashCode(); } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/BufferLookupFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/BufferLookupFieldDescription.cs index 0683b02..9770681 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/BufferLookupFieldDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/BufferLookupFieldDescription.cs @@ -1,38 +1,45 @@ using System; +using System.CodeDom.Compiler; using Microsoft.CodeAnalysis; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public readonly struct BufferLookupFieldDescription : IEquatable, IMemberDescription { - public readonly struct BufferLookupFieldDescription : IEquatable, INonQueryFieldDescription - { - ITypeSymbol TypeSymbol { get; } - bool IsReadOnly { get; } - public string GeneratedFieldName { get; } + ITypeSymbol TypeSymbol { get; } + bool IsReadOnly { get; } + public string GeneratedFieldName { get; } - public string GetFieldDeclaration(bool forcePublic = false) => - $"{"[global::Unity.Collections.ReadOnly] ".EmitIfTrue(IsReadOnly)} {(forcePublic ? "public" : "")} Unity.Entities.BufferLookup<{TypeSymbol.ToFullName()}> {GeneratedFieldName};"; - public string GetFieldAssignment() => - $@"{GeneratedFieldName} = state.GetBufferLookup<{TypeSymbol.ToFullName()}>({(IsReadOnly ? "true" : "false")});"; + public void AppendMemberDeclaration(IndentedTextWriter w, bool forcePublic = false) + { + if (IsReadOnly) + w.Write("[global::Unity.Collections.ReadOnly] "); + if (forcePublic) + w.Write("public "); + w.Write($"Unity.Entities.BufferLookup<{TypeSymbol.ToFullName()}> {GeneratedFieldName};"); + w.WriteLine(); + } + public string GetMemberAssignment() => + $@"{GeneratedFieldName} = state.GetBufferLookup<{TypeSymbol.ToFullName()}>({(IsReadOnly ? "true" : "false")});"; - public BufferLookupFieldDescription(ITypeSymbol typeSymbol, bool isReadOnly) - { - TypeSymbol = typeSymbol; - IsReadOnly = isReadOnly; + public BufferLookupFieldDescription(ITypeSymbol typeSymbol, bool isReadOnly) + { + TypeSymbol = typeSymbol; + IsReadOnly = isReadOnly; - GeneratedFieldName = $"__{TypeSymbol.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_BufferLookup"; - } + GeneratedFieldName = $"__{TypeSymbol.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_BufferLookup"; + } - public bool Equals(BufferLookupFieldDescription other) => - SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) && IsReadOnly == other.IsReadOnly; + public bool Equals(BufferLookupFieldDescription other) => + SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) && IsReadOnly == other.IsReadOnly; - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - return ((TypeSymbol != null ? - SymbolEqualityComparer.Default.GetHashCode(TypeSymbol) : 0) * 397) ^ IsReadOnly.GetHashCode(); - } + return ((TypeSymbol != null ? + SymbolEqualityComparer.Default.GetHashCode(TypeSymbol) : 0) * 397) ^ IsReadOnly.GetHashCode(); } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/ComponentLookupFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/ComponentLookupFieldDescription.cs index ee3fc29..8553f1c 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/ComponentLookupFieldDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/ComponentLookupFieldDescription.cs @@ -1,38 +1,46 @@ using System; +using System.CodeDom.Compiler; using Microsoft.CodeAnalysis; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public readonly struct ComponentLookupFieldDescription : IEquatable, IMemberDescription { - public readonly struct ComponentLookupFieldDescription : IEquatable, INonQueryFieldDescription + ITypeSymbol TypeSymbol { get; } + bool IsReadOnly { get; } + public string GeneratedFieldName{ get; } + + public void AppendMemberDeclaration(IndentedTextWriter w, bool forcePublic = false) { - ITypeSymbol TypeSymbol { get; } - bool IsReadOnly { get; } - public string GeneratedFieldName{ get; } + if (IsReadOnly) + w.Write("[global::Unity.Collections.ReadOnly] "); + if (forcePublic) + w.Write("public "); + w.Write($"Unity.Entities.ComponentLookup<{TypeSymbol.ToFullName()}> {GeneratedFieldName};"); + w.WriteLine(); + } - public string GetFieldDeclaration(bool forcePublic = false) => - $"{"[global::Unity.Collections.ReadOnly] ".EmitIfTrue(IsReadOnly)} {(forcePublic ? "public" : "")} Unity.Entities.ComponentLookup<{TypeSymbol.ToFullName()}> {GeneratedFieldName};"; - public string GetFieldAssignment() => - $@"{GeneratedFieldName} = state.GetComponentLookup<{TypeSymbol.ToFullName()}>({(IsReadOnly ? "true" : "false")});"; + public string GetMemberAssignment() => + $@"{GeneratedFieldName} = state.GetComponentLookup<{TypeSymbol.ToFullName()}>({(IsReadOnly ? "true" : "false")});"; - public ComponentLookupFieldDescription(ITypeSymbol typeSymbol, bool isReadOnly) - { - TypeSymbol = typeSymbol; - IsReadOnly = isReadOnly; + public ComponentLookupFieldDescription(ITypeSymbol typeSymbol, bool isReadOnly) + { + TypeSymbol = typeSymbol; + IsReadOnly = isReadOnly; - GeneratedFieldName = $"__{TypeSymbol.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_ComponentLookup"; - } + GeneratedFieldName = $"__{TypeSymbol.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_ComponentLookup"; + } - public bool Equals(ComponentLookupFieldDescription other) => - SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) && IsReadOnly == other.IsReadOnly; + public bool Equals(ComponentLookupFieldDescription other) => + SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) && IsReadOnly == other.IsReadOnly; - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - return ((TypeSymbol != null ? - SymbolEqualityComparer.Default.GetHashCode(TypeSymbol) : 0) * 397) ^ IsReadOnly.GetHashCode(); - } + return ((TypeSymbol != null ? + SymbolEqualityComparer.Default.GetHashCode(TypeSymbol) : 0) * 397) ^ IsReadOnly.GetHashCode(); } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/ContainerTypeHandleFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/ContainerTypeHandleFieldDescription.cs index 305bb3d..a574cae 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/ContainerTypeHandleFieldDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/ContainerTypeHandleFieldDescription.cs @@ -1,24 +1,29 @@ using System; +using System.CodeDom.Compiler; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public readonly struct ContainerTypeHandleFieldDescription : IEquatable, IMemberDescription { - public readonly struct ContainerTypeHandleFieldDescription : IEquatable, INonQueryFieldDescription + string ContainerTypeName { get; } + public string GeneratedFieldName { get; } + public void AppendMemberDeclaration(IndentedTextWriter w, bool forcePublic = false) { - string ContainerTypeName { get; } - public string GeneratedFieldName { get; } - - public string GetFieldDeclaration(bool forcePublic = false) => - $"{(forcePublic ? "public" : "")} {ContainerTypeName}.TypeHandle {GeneratedFieldName};"; - public string GetFieldAssignment() - => $@"{GeneratedFieldName} = new {ContainerTypeName}.TypeHandle(ref state, isReadOnly: false);"; + if (forcePublic) + w.Write("public "); + w.Write($"{ContainerTypeName}.TypeHandle {GeneratedFieldName};"); + w.WriteLine(); + } - public ContainerTypeHandleFieldDescription(string containerTypeName) - { - ContainerTypeName = containerTypeName; - GeneratedFieldName = $"__{containerTypeName.Replace(".", "_")}_RW_TypeHandle"; - } + public string GetMemberAssignment() + => $@"{GeneratedFieldName} = new {ContainerTypeName}.TypeHandle(ref state, isReadOnly: false);"; - public bool Equals(ContainerTypeHandleFieldDescription other) => ContainerTypeName == other.ContainerTypeName; - public override int GetHashCode() => ContainerTypeName != null ? ContainerTypeName.GetHashCode() : 0; + public ContainerTypeHandleFieldDescription(string containerTypeName) + { + ContainerTypeName = containerTypeName; + GeneratedFieldName = $"__{containerTypeName.Replace(".", "_")}_RW_TypeHandle"; } + + public bool Equals(ContainerTypeHandleFieldDescription other) => ContainerTypeName == other.ContainerTypeName; + public override int GetHashCode() => ContainerTypeName != null ? ContainerTypeName.GetHashCode() : 0; } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/EntityStorageInfoLookupFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/EntityStorageInfoLookupFieldDescription.cs index 2bb6042..b6bc716 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/EntityStorageInfoLookupFieldDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/EntityStorageInfoLookupFieldDescription.cs @@ -1,15 +1,21 @@ using System; +using System.CodeDom.Compiler; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public struct EntityStorageInfoLookupFieldDescription : IEquatable, IMemberDescription { - public struct EntityStorageInfoLookupFieldDescription : IEquatable, INonQueryFieldDescription + public string GeneratedFieldName => "__EntityStorageInfoLookup"; + public void AppendMemberDeclaration(IndentedTextWriter w, bool forcePublic = false) { - public string GeneratedFieldName => "__EntityStorageInfoLookup"; - public string GetFieldDeclaration(bool forcePublic = false) => - $"[global::Unity.Collections.ReadOnly] {(forcePublic ? "public" : "")} Unity.Entities.EntityStorageInfoLookup {GeneratedFieldName};"; - public string GetFieldAssignment() => - $@"{GeneratedFieldName} = state.GetEntityStorageInfoLookup();"; - public bool Equals(EntityStorageInfoLookupFieldDescription other) => true; - public override int GetHashCode() => 0; + w.Write("[global::Unity.Collections.ReadOnly] "); + if (forcePublic) + w.Write("public "); + w.Write($"Unity.Entities.EntityStorageInfoLookup {GeneratedFieldName};"); + w.WriteLine(); } + public string GetMemberAssignment() => + $@"{GeneratedFieldName} = state.GetEntityStorageInfoLookup();"; + public bool Equals(EntityStorageInfoLookupFieldDescription other) => true; + public override int GetHashCode() => 0; } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/EntityTypeHandleFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/EntityTypeHandleFieldDescription.cs index 3c54f3b..22fc61e 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/EntityTypeHandleFieldDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/EntityTypeHandleFieldDescription.cs @@ -1,14 +1,20 @@ using System; +using System.CodeDom.Compiler; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public readonly struct EntityTypeHandleFieldDescription : IMemberDescription, IEquatable { - public readonly struct EntityTypeHandleFieldDescription : INonQueryFieldDescription, IEquatable + public string GeneratedFieldName => "__Unity_Entities_Entity_TypeHandle"; + public void AppendMemberDeclaration(IndentedTextWriter w, bool forcePublic = false) { - public string GeneratedFieldName => "__Unity_Entities_Entity_TypeHandle"; - public string GetFieldDeclaration(bool forcePublic = false) => - $"[global::Unity.Collections.ReadOnly] {(forcePublic ? "public" : "")} global::Unity.Entities.EntityTypeHandle {GeneratedFieldName};"; - public string GetFieldAssignment() => $@"{GeneratedFieldName} = state.GetEntityTypeHandle();"; - public bool Equals(EntityTypeHandleFieldDescription other) => true; - public override int GetHashCode() => GeneratedFieldName.GetHashCode(); + w.Write("[global::Unity.Collections.ReadOnly] "); + if (forcePublic) + w.Write("public "); + w.Write($"global::Unity.Entities.EntityTypeHandle {GeneratedFieldName};"); + w.WriteLine(); } + public string GetMemberAssignment() => $@"{GeneratedFieldName} = state.GetEntityTypeHandle();"; + public bool Equals(EntityTypeHandleFieldDescription other) => true; + public override int GetHashCode() => GeneratedFieldName.GetHashCode(); } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/INonQueryFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/INonQueryFieldDescription.cs deleted file mode 100644 index 2b03091..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/INonQueryFieldDescription.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Unity.Entities.SourceGen.SystemGenerator.Common -{ - public interface INonQueryFieldDescription - { - string GetFieldDeclaration(bool forcePublic = false); - string GetFieldAssignment(); - string GeneratedFieldName { get; } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/JobEntityQueryAndHandleDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/JobEntityQueryAndHandleDescription.cs index c62c7d0..734421d 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/JobEntityQueryAndHandleDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/JobEntityQueryAndHandleDescription.cs @@ -1,38 +1,43 @@ using System; +using System.CodeDom.Compiler; using Microsoft.CodeAnalysis; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public readonly struct JobEntityQueryAndHandleDescription : IEquatable, IMemberDescription { - public readonly struct JobEntityQueryAndHandleDescription : IEquatable, INonQueryFieldDescription + ITypeSymbol TypeSymbol { get; } + bool AssignDefaultQuery { get; } + public string GeneratedFieldName{ get; } + + public void AppendMemberDeclaration(IndentedTextWriter w, bool forcePublic = false) { - ITypeSymbol TypeSymbol { get; } - bool AssignDefaultQuery { get; } - public string GeneratedFieldName{ get; } + w.Write("public "); + w.Write($"{TypeSymbol.ToFullName()}.InternalCompilerQueryAndHandleData {GeneratedFieldName};"); + w.WriteLine(); + } - public string GetFieldDeclaration(bool forcePublic = false) => - $"{(forcePublic ? "public" : "")} {TypeSymbol.ToFullName()}.InternalCompilerQueryAndHandleData {GeneratedFieldName};"; - public string GetFieldAssignment() => - $@"{GeneratedFieldName}.Init(ref state, {(AssignDefaultQuery ? "true" : "false")});"; + public string GetMemberAssignment() => + $@"{GeneratedFieldName}.Init(ref state, {(AssignDefaultQuery ? "true" : "false")});"; - public JobEntityQueryAndHandleDescription(ITypeSymbol typeSymbol, bool assignDefaultQuery) - { - TypeSymbol = typeSymbol; - AssignDefaultQuery = assignDefaultQuery; + public JobEntityQueryAndHandleDescription(ITypeSymbol typeSymbol, bool assignDefaultQuery) + { + TypeSymbol = typeSymbol; + AssignDefaultQuery = assignDefaultQuery; - GeneratedFieldName = $"__{TypeSymbol.ToValidIdentifier()}_{(AssignDefaultQuery ? "With" : "Without")}DefaultQuery_JobEntityTypeHandle"; - } + GeneratedFieldName = $"__{TypeSymbol.ToValidIdentifier()}_{(AssignDefaultQuery ? "With" : "Without")}DefaultQuery_JobEntityTypeHandle"; + } - public bool Equals(JobEntityQueryAndHandleDescription other) => - SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) && AssignDefaultQuery == other.AssignDefaultQuery; + public bool Equals(JobEntityQueryAndHandleDescription other) => + SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) && AssignDefaultQuery == other.AssignDefaultQuery; - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - return ((TypeSymbol != null ? - SymbolEqualityComparer.Default.GetHashCode(TypeSymbol) : 0) * 397) ^ AssignDefaultQuery.GetHashCode(); - } + return ((TypeSymbol != null ? + SymbolEqualityComparer.Default.GetHashCode(TypeSymbol) : 0) * 397) ^ AssignDefaultQuery.GetHashCode(); } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/MemberDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/MemberDescription.cs new file mode 100644 index 0000000..b087a9a --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/MemberDescription.cs @@ -0,0 +1,32 @@ +using System; +using System.CodeDom.Compiler; + +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public interface IMemberWriter +{ + public void WriteTo(IndentedTextWriter writer); +} + +public interface IMemberDescription +{ + string GeneratedFieldName { get; } + void AppendMemberDeclaration(IndentedTextWriter w, bool forcePublic = false); + string GetMemberAssignment(); +} + +public class MemberDescription : IMemberDescription, IEquatable +{ + private readonly IMemberWriter _memberWriter; + + public MemberDescription(IMemberWriter memberWriter) => _memberWriter = memberWriter; + + public string GeneratedFieldName => string.Empty; + public void AppendMemberDeclaration(IndentedTextWriter w, bool forcePublic = false) => _memberWriter.WriteTo(w); + public string GetMemberAssignment() => string.Empty; + + + public bool Equals(MemberDescription other) => other != null && GetType() == other.GetType(); + + public override int GetHashCode() => typeof(MemberDescription).GetHashCode(); +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/TypeHandleFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/TypeHandleFieldDescription.cs index 764e9dd..839a608 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/TypeHandleFieldDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/INonQueryFieldDescriptions/TypeHandleFieldDescription.cs @@ -1,84 +1,114 @@ using System; +using System.CodeDom.Compiler; using Microsoft.CodeAnalysis; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public readonly struct TypeHandleFieldDescription : IEquatable, IMemberDescription { - public readonly struct TypeHandleFieldDescription : IEquatable, INonQueryFieldDescription + public enum TypeHandleSource + { + Aspect, + Component, + SharedComponent, + BufferElement + } + ITypeSymbol TypeSymbol { get; } + bool IsReadOnly { get; } + + TypeHandleSource Source { get; } // I'm sure we know the type at the call site, + // lets either split this into four classes + // or see if we can find a way to + // not have a bazillion INonQueryFieldDescription + public string GeneratedFieldName { get; } + + public void AppendMemberDeclaration(IndentedTextWriter w, bool forcePublic = false) { - enum TypeHandleSource + switch (Source) { - Aspect, - Component, - SharedComponent, - BufferElement + case TypeHandleSource.Aspect: + if (IsReadOnly) + w.Write("[global::Unity.Collections.ReadOnly] "); + if (forcePublic) + w.Write("public "); + w.Write($"{TypeSymbol.ToFullName()}.TypeHandle {GeneratedFieldName};"); + break; + case TypeHandleSource.BufferElement: + if (IsReadOnly) + w.Write("[global::Unity.Collections.ReadOnly] "); + if (forcePublic) + w.Write("public "); + w.Write($"Unity.Entities.BufferTypeHandle<{TypeSymbol.ToFullName()}> {GeneratedFieldName};"); + break; + case TypeHandleSource.Component: + if (IsReadOnly) + w.Write("[global::Unity.Collections.ReadOnly] "); + if (forcePublic) + w.Write("public "); + w.Write($"Unity.Entities.ComponentTypeHandle<{TypeSymbol.ToFullName()}> {GeneratedFieldName};"); + break; + default: + if (IsReadOnly) + w.Write("[global::Unity.Collections.ReadOnly] "); + if (forcePublic) + w.Write("public "); + w.Write($"Unity.Entities.SharedComponentTypeHandle<{TypeSymbol.ToFullName()}> {GeneratedFieldName};"); + break; } - ITypeSymbol TypeSymbol { get; } - bool IsReadOnly { get; } - TypeHandleSource Source { get; } // I'm sure we know the type at the call site, - // lets either split this into four classes - // or see if we can find a way to - // not have a bazillion INonQueryFieldDescription - public string GeneratedFieldName { get; } + w.WriteLine(); + } - public string GetFieldDeclaration(bool forcePublic = false) => Source switch - { - TypeHandleSource.Aspect => $"{"[global::Unity.Collections.ReadOnly] ".EmitIfTrue(IsReadOnly)} {(forcePublic ? "public" : "")} {TypeSymbol.ToFullName()}.TypeHandle {GeneratedFieldName};", - TypeHandleSource.BufferElement => $"{"[global::Unity.Collections.ReadOnly] ".EmitIfTrue(IsReadOnly)} {(forcePublic ? "public" : "")} Unity.Entities.BufferTypeHandle<{TypeSymbol.ToFullName()}> {GeneratedFieldName};", - TypeHandleSource.Component => $"{"[global::Unity.Collections.ReadOnly] ".EmitIfTrue(IsReadOnly)} {(forcePublic ? "public" : "")} Unity.Entities.ComponentTypeHandle<{TypeSymbol.ToFullName()}> {GeneratedFieldName};", - _ => $"{"[global::Unity.Collections.ReadOnly] ".EmitIfTrue(IsReadOnly)} {(forcePublic ? "public" : "")} Unity.Entities.SharedComponentTypeHandle<{TypeSymbol.ToFullName()}> {GeneratedFieldName};", - }; - public string GetFieldAssignment() => Source switch - { - TypeHandleSource.Aspect => $"{GeneratedFieldName} = new {TypeSymbol.ToFullName()}.TypeHandle(ref state);", - TypeHandleSource.BufferElement => $"{GeneratedFieldName} = state.GetBufferTypeHandle<{TypeSymbol.ToFullName()}>({(IsReadOnly ? "true" : "false")});", - TypeHandleSource.Component => - TypeSymbol.IsReferenceType - ? $"{GeneratedFieldName} = state.EntityManager.GetComponentTypeHandle<{TypeSymbol.ToFullName()}>({(IsReadOnly ? "true" : "false")});" - : $"{GeneratedFieldName} = state.GetComponentTypeHandle<{TypeSymbol.ToFullName()}>({(IsReadOnly ? "true" : "false")});", - _ => $"{GeneratedFieldName} = state.GetSharedComponentTypeHandle<{TypeSymbol.ToFullName()}>();" - }; + public string GetMemberAssignment() => Source switch + { + TypeHandleSource.Aspect => $"{GeneratedFieldName} = new {TypeSymbol.ToFullName()}.TypeHandle(ref state);", + TypeHandleSource.BufferElement => $"{GeneratedFieldName} = state.GetBufferTypeHandle<{TypeSymbol.ToFullName()}>({(IsReadOnly ? "true" : "false")});", + TypeHandleSource.Component => + TypeSymbol.IsReferenceType + ? $"{GeneratedFieldName} = state.EntityManager.GetComponentTypeHandle<{TypeSymbol.ToFullName()}>({(IsReadOnly ? "true" : "false")});" + : $"{GeneratedFieldName} = state.GetComponentTypeHandle<{TypeSymbol.ToFullName()}>({(IsReadOnly ? "true" : "false")});", + _ => $"{GeneratedFieldName} = state.GetSharedComponentTypeHandle<{TypeSymbol.ToFullName()}>();" + }; - public TypeHandleFieldDescription(ITypeSymbol typeSymbol, bool isReadOnly) - { - TypeSymbol = typeSymbol; - IsReadOnly = isReadOnly; + public TypeHandleFieldDescription(ITypeSymbol typeSymbol, bool isReadOnly) + { + TypeSymbol = typeSymbol; + IsReadOnly = isReadOnly; - var typeSymbolValidIdentifier = TypeSymbol.ToValidIdentifier(); - if (TypeSymbol.IsAspect()) - { - GeneratedFieldName = $"__{typeSymbolValidIdentifier}_{(IsReadOnly ? "RO" : "RW")}_AspectTypeHandle"; - Source = TypeHandleSource.Aspect; - } - else if (typeSymbol.InheritsFromInterface("Unity.Entities.IBufferElementData")) - { - GeneratedFieldName = $"__{typeSymbolValidIdentifier}_{(IsReadOnly ? "RO" : "RW")}_BufferTypeHandle"; - Source = TypeHandleSource.BufferElement; - } - else if (typeSymbol.IsSharedComponent()) - { - GeneratedFieldName = $"__{typeSymbolValidIdentifier}_SharedComponentTypeHandle"; - Source = TypeHandleSource.SharedComponent; - } - else - { - GeneratedFieldName = $"__{typeSymbolValidIdentifier}_{(IsReadOnly ? "RO" : "RW")}_ComponentTypeHandle"; - Source = TypeHandleSource.Component; - } + var typeSymbolValidIdentifier = TypeSymbol.ToValidIdentifier(); + if (TypeSymbol.IsAspect()) + { + GeneratedFieldName = $"__{typeSymbolValidIdentifier}_{(IsReadOnly ? "RO" : "RW")}_AspectTypeHandle"; + Source = TypeHandleSource.Aspect; + } + else if (typeSymbol.InheritsFromInterface("Unity.Entities.IBufferElementData")) + { + GeneratedFieldName = $"__{typeSymbolValidIdentifier}_{(IsReadOnly ? "RO" : "RW")}_BufferTypeHandle"; + Source = TypeHandleSource.BufferElement; + } + else if (typeSymbol.IsSharedComponent()) + { + GeneratedFieldName = $"__{typeSymbolValidIdentifier}_SharedComponentTypeHandle"; + Source = TypeHandleSource.SharedComponent; } + else + { + GeneratedFieldName = $"__{typeSymbolValidIdentifier}_{(IsReadOnly ? "RO" : "RW")}_ComponentTypeHandle"; + Source = TypeHandleSource.Component; + } + } - public bool Equals(TypeHandleFieldDescription other) => - SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) && IsReadOnly == other.IsReadOnly && Source == other.Source; + public bool Equals(TypeHandleFieldDescription other) => + SymbolEqualityComparer.Default.Equals(TypeSymbol, other.TypeSymbol) && IsReadOnly == other.IsReadOnly && Source == other.Source; - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - var hashCode = TypeSymbol != null ? SymbolEqualityComparer.Default.GetHashCode(TypeSymbol) : 0; - hashCode = (hashCode * 397) ^ IsReadOnly.GetHashCode(); - hashCode = (hashCode * 397) ^ (int)Source; - return hashCode; - } + var hashCode = TypeSymbol != null ? SymbolEqualityComparer.Default.GetHashCode(TypeSymbol) : 0; + hashCode = (hashCode * 397) ^ IsReadOnly.GetHashCode(); + hashCode = (hashCode * 397) ^ (int)Source; + return hashCode; } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/IQueryFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/IQueryFieldDescription.cs deleted file mode 100644 index efe9946..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/IQueryFieldDescription.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Unity.Entities.SourceGen.SystemGenerator.Common -{ - public interface IQueryFieldDescription - { - public string EntityQueryFieldAssignment(string generatedQueryFieldName); - public string GetFieldDeclaration(string generatedQueryFieldName, bool forcePublic = false); - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/ISystemCandidate.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/ISystemCandidate.cs deleted file mode 100644 index 814942a..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/ISystemCandidate.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.SystemGenerator; -using Unity.Entities.SourceGen.SystemGenerator.Common; - -namespace Unity.Entities.SourceGen.Common -{ - public interface ISystemCandidate - { - public string CandidateTypeName { get; } - public SyntaxNode Node { get; } - } - - public interface IAdditionalHandlesInfo - { - SemanticModel SemanticModel { get; } - SystemType SystemType { get; } - TypeDeclarationSyntax TypeSyntax { get; } - } - - public static class NodeContainerExtensions - { - public static bool TryGetSystemStateParameterName(this T1 desc, T2 candidate, out ExpressionSyntax systemStateExpression) where T1 : ISourceGeneratorDiagnosable, IAdditionalHandlesInfo where T2 : ISystemCandidate - { - switch (desc.SystemType) - { - case SystemType.ISystem: - { - var methodDeclarationSyntax = candidate.Node.AncestorOfKindOrDefault(); - if (methodDeclarationSyntax == null) { - SystemGeneratorErrors.SGSG0001(desc, candidate); - systemStateExpression = null; - return false; - } - var containingMethodSymbol = desc.SemanticModel.GetDeclaredSymbol(methodDeclarationSyntax); - var systemStateParameterName = containingMethodSymbol?.Parameters.FirstOrDefault(p => p.Type.Is("Unity.Entities.SystemState"))?.Name; - if (systemStateParameterName != null) - { - systemStateExpression = SyntaxFactory.IdentifierName(systemStateParameterName); - return true; - } - - SystemGeneratorErrors.SGSG0002(desc, candidate); - systemStateExpression = null; - return false; - } - case SystemType.Unknown: - systemStateExpression = SyntaxFactory.IdentifierName("state"); - return true; - } - - // this.CheckedStateRef - systemStateExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName("CheckedStateRef")); - return true; - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/ISystemModule.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/ISystemModule.cs index 0e62e20..5c61446 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/ISystemModule.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/ISystemModule.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; namespace Unity.Entities.SourceGen.SystemGenerator.Common { @@ -9,7 +10,7 @@ public interface ISystemModule IEnumerable<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)> Candidates { get; } bool RequiresReferenceToBurst { get; } - void OnReceiveSyntaxNode(SyntaxNode node); + void OnReceiveSyntaxNode(SyntaxNode node, Dictionary candidateOwnership); bool RegisterChangesInSystem(SystemDescription systemDescription); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/MultipleArchetypeQueryFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/MultipleArchetypeQueryFieldDescription.cs index 5862e59..dc5d441 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/MultipleArchetypeQueryFieldDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/MultipleArchetypeQueryFieldDescription.cs @@ -1,54 +1,55 @@ using System; +using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; +using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.Common; + +public readonly struct MultipleArchetypeQueryFieldDescription : IEquatable, IQueryFieldDescription { - public readonly struct MultipleArchetypeQueryFieldDescription : IEquatable, IQueryFieldDescription - { - readonly IReadOnlyCollection _archetypes; - readonly string _entityQueryBuilderBodyBeforeBuild; + readonly IReadOnlyCollection _archetypes; + readonly string _entityQueryBuilderBodyBeforeBuild; - public MultipleArchetypeQueryFieldDescription(IReadOnlyCollection archetypes, string entityQueryBuilderBodyBeforeBuild) - { - _archetypes = archetypes; - _entityQueryBuilderBodyBeforeBuild = entityQueryBuilderBodyBeforeBuild; - } + public MultipleArchetypeQueryFieldDescription(IReadOnlyCollection archetypes, string entityQueryBuilderBodyBeforeBuild) + { + _archetypes = archetypes; + _entityQueryBuilderBodyBeforeBuild = entityQueryBuilderBodyBeforeBuild; + } - public string EntityQueryFieldAssignment(string generatedQueryFieldName) - { - var buildInvocation = $"entityQueryBuilder{_entityQueryBuilderBodyBeforeBuild}.Build(ref state);"; - return $"{generatedQueryFieldName} = {buildInvocation}{Environment.NewLine}entityQueryBuilder.Reset();"; - } + public void WriteEntityQueryFieldAssignment(IndentedTextWriter writer, string generatedQueryFieldName) + { + writer.WriteLine($"{generatedQueryFieldName} = entityQueryBuilder{_entityQueryBuilderBodyBeforeBuild}.Build(ref state);"); + writer.WriteLine("entityQueryBuilder.Reset();"); + } - public string GetFieldDeclaration(string generatedQueryFieldName, bool forcePublic = false) - => $"{(forcePublic ? "public" : "")} Unity.Entities.EntityQuery {generatedQueryFieldName};"; + public string GetFieldDeclaration(string generatedQueryFieldName, bool forcePublic = false) + => $"{(forcePublic ? "public" : "")} Unity.Entities.EntityQuery {generatedQueryFieldName};"; - public bool Equals(MultipleArchetypeQueryFieldDescription other) - => _archetypes.SequenceEqual(other._archetypes); + public bool Equals(MultipleArchetypeQueryFieldDescription other) + => _archetypes.SequenceEqual(other._archetypes); - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) - return false; + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) + return false; - return obj.GetType() == GetType() && Equals((MultipleArchetypeQueryFieldDescription)obj); - } + return obj.GetType() == GetType() && Equals((MultipleArchetypeQueryFieldDescription)obj); + } - public override int GetHashCode() + public override int GetHashCode() + { + unchecked { - unchecked - { - int hash = 19; + int hash = 19; - foreach (var archetype in _archetypes) - hash = hash * 31 + archetype.GetHashCode(); + foreach (var archetype in _archetypes) + hash = hash * 31 + archetype.GetHashCode(); - return hash; - } + return hash; } - - public static bool operator ==(MultipleArchetypeQueryFieldDescription left, MultipleArchetypeQueryFieldDescription right) => Equals(left, right); - public static bool operator !=(MultipleArchetypeQueryFieldDescription left, MultipleArchetypeQueryFieldDescription right) => !Equals(left, right); } + + public static bool operator ==(MultipleArchetypeQueryFieldDescription left, MultipleArchetypeQueryFieldDescription right) => Equals(left, right); + public static bool operator !=(MultipleArchetypeQueryFieldDescription left, MultipleArchetypeQueryFieldDescription right) => !Equals(left, right); } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/PartialSystemTypeGenerator.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/PartialSystemTypeGenerator.cs index 18c6818..f970a62 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/PartialSystemTypeGenerator.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/PartialSystemTypeGenerator.cs @@ -1,230 +1,228 @@ -using System.Collections.Generic; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.IO; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; -using static Unity.Entities.SourceGen.Common.SourceGenHelpers; namespace Unity.Entities.SourceGen.SystemGenerator.Common { public static class PartialSystemTypeGenerator { - public static (SyntaxTreeInfo SyntaxTreeInfo, TypeDeclarationSyntax OriginalSystem, TypeDeclarationSyntax GeneratedPartialSystem) - Generate(SystemDescription[] allDescriptionsForTheSameSystem) + static (int NumClosingBracesRequired, int NumNotClosedIfDirectives) + WriteOpeningSyntaxForGeneratedPart_AndReturnClosingSyntax( + IndentedTextWriter writer, + SyntaxNode originalType) { - var description = allDescriptionsForTheSameSystem.First(); + static (Stack<(string Value, bool AddIndentAfter)> OpeningSyntaxes, int NumClosingBracesRequired) GetBaseDeclaration(SyntaxNode typeSyntax) + { + var opening = new Stack<(string Value, bool AddIndentAfter)>(); + var numBracesToClose = 0; + var parentSyntax = typeSyntax.Parent as MemberDeclarationSyntax; + + while (parentSyntax != null && ( + parentSyntax.IsKind(SyntaxKind.ClassDeclaration) || + parentSyntax.IsKind(SyntaxKind.StructDeclaration) || + parentSyntax.IsKind(SyntaxKind.RecordDeclaration) || + parentSyntax.IsKind(SyntaxKind.NamespaceDeclaration))) + { + switch (parentSyntax) + { + case TypeDeclarationSyntax parentTypeSyntax: + var keyword = parentTypeSyntax.Keyword.ValueText; // e.g. class/struct/record + var typeName = parentTypeSyntax.Identifier.ToString() + parentTypeSyntax.TypeParameterList; // e.g. Outer/Generic + var constraint = parentTypeSyntax.ConstraintClauses.ToString(); // e.g. where T: new() + opening.Push(("{", AddIndentAfter: true)); + opening.Push(($"partial {keyword} {typeName} {constraint}", AddIndentAfter: false)); + break; + case NamespaceDeclarationSyntax parentNameSpaceSyntax: + opening.Push(($"{parentNameSpaceSyntax.Usings}", AddIndentAfter: false)); + opening.Push(("{", AddIndentAfter: true)); + opening.Push(($"namespace {parentNameSpaceSyntax.Name}", AddIndentAfter: false)); + break; + } + + numBracesToClose++; + parentSyntax = parentSyntax.Parent as MemberDeclarationSyntax; + } + return (opening, numBracesToClose); + } - var generatedPartialType = CreatePartialSystemType(allDescriptionsForTheSameSystem, description.SystemTypeSyntax); - generatedPartialType = ReplaceNodesInSystem(generatedPartialType, allDescriptionsForTheSameSystem); - generatedPartialType = AddMiscellaneousMembers(generatedPartialType, allDescriptionsForTheSameSystem); - generatedPartialType = AddEntityCommandBufferSystemFields(generatedPartialType, allDescriptionsForTheSameSystem); - generatedPartialType = AddOnCreateForCompilerWithFields(generatedPartialType, allDescriptionsForTheSameSystem, description.SystemType == SystemType.ISystem); + var baseDeclaration = GetBaseDeclaration(originalType); + var usings = originalType.SyntaxTree.GetCompilationUnitRoot().Usings; - return (description.SyntaxTreeInfo, description.SystemTypeSyntax, generatedPartialType); - } + int numNotClosedIfDirectives = 0; + foreach (var @using in usings) + if (@using.ContainsDirectives) + { + foreach (var token in @using.ChildTokens()) + foreach (var trivia in token.LeadingTrivia) + if (trivia.IsDirective) + { + writer.WriteLine(trivia.ToString()); + if (trivia.IsKind(SyntaxKind.IfDirectiveTrivia)) + numNotClosedIfDirectives++; + else if (trivia.IsKind(SyntaxKind.EndIfDirectiveTrivia)) + numNotClosedIfDirectives--; + } + } - private static TypeDeclarationSyntax AddMiscellaneousMembers(TypeDeclarationSyntax generatedPartialType, SystemDescription[] descriptions) - => generatedPartialType.AddMembers(descriptions.SelectMany(d=>d.NewMiscellaneousMembers).ToArray()); + foreach (var usingDirectiveSyntax in usings) + writer.WriteLine(usingDirectiveSyntax.ToString()); - private static TypeDeclarationSyntax AddEntityCommandBufferSystemFields(TypeDeclarationSyntax generatedPartialType, SystemDescription[] descriptions) - { - foreach (var kvp in descriptions.SelectMany(d => d.FullEcbSystemTypeNamesToGeneratedFieldNames).DistinctBy(kvp => kvp.Key)) + foreach (var opening in baseDeclaration.OpeningSyntaxes) { - var field = SyntaxFactory.ParseMemberDeclaration($"{kvp.Key} {kvp.Value};"); - if (field != null) - generatedPartialType = generatedPartialType.AddMembers(field); + writer.WriteLine(opening.Value); + if (opening.AddIndentAfter) + writer.Indent++; } - return generatedPartialType; + return (baseDeclaration.NumClosingBracesRequired, numNotClosedIfDirectives); } - private static TypeDeclarationSyntax AddOnCreateForCompilerWithFields(TypeDeclarationSyntax generatedPartialType, SystemDescription[] descriptions, bool isISystem) + public static (SyntaxTreeInfo SyntaxTreeInfo, TypeDeclarationSyntax OriginalSystem, string GeneratedSyntaxTreeContainingGeneratedPartialSystem) + Generate(SystemDescription[] allDescriptionsForTheSameSystem) { - // Only needs to create OnCreateForCompiler in SystemBase if members are present - if (!descriptions.Any(d => d.HandlesDescription.NonQueryFields.Count > 0 || d.HandlesDescription.QueryFieldsToFieldNames.Count > 0 || d.AdditionalStatementsInOnCreateForCompilerMethod.Count > 0)) - return isISystem ? generatedPartialType.AddMembers(EntitiesSourceFactory.OnCreateForCompilerStub()) : generatedPartialType; + var description = allDescriptionsForTheSameSystem.First(); - var typeHandle = HandlesDescription.GetTypeHandleForInitialPartial(false, false, "", descriptions.Select(d=>d.HandlesDescription).ToArray()); - var typeHandleSyntaxMember = SyntaxFactory.ParseMemberDeclaration(typeHandle); - if (typeHandleSyntaxMember is TypeDeclarationSyntax typeHandleSyntax) - generatedPartialType = generatedPartialType.AddMembers(typeHandleSyntax.Members.ToArray()); + using var sw = new StringWriter(); + using var indentedTextWriter = new IndentedTextWriter(sw); - var additionalSyntax = descriptions.SelectMany(d => d.AdditionalStatementsInOnCreateForCompilerMethod).Distinct().ToArray(); - generatedPartialType = generatedPartialType.AddMembers(EntitiesSourceFactory.OnCreateForCompilerMethod(additionalSyntax, isISystem)); + var result = WriteOpeningSyntaxForGeneratedPart_AndReturnClosingSyntax(indentedTextWriter, description.SystemTypeSyntax); + AppendBeginSystemType(indentedTextWriter, description.SystemTypeSyntax); - return generatedPartialType; - } + foreach (var desc in allDescriptionsForTheSameSystem) + { + var walker = new SystemSyntaxWalker(desc); - private static TypeDeclarationSyntax CreatePartialSystemType(SystemDescription[] descriptions, TypeDeclarationSyntax originalSyntax) - { - var allFullyQualifiedBaseTypeNames = descriptions.SelectMany(desc => desc.FullyQualifiedBaseTypeNames).Distinct(); + foreach (var kvp in desc.CandidateNodesGroupedByMethodOrProperty) + { + switch (kvp.Key) + { + case MethodDeclarationSyntax methodDeclarationSyntax: + { + var visit = walker.VisitMethodDeclarationInSystem(methodDeclarationSyntax); + if (visit.MethodRequiresSourceGen) + indentedTextWriter.WriteLine(visit.SourceGeneratedMethod); + break; + } + case PropertyDeclarationSyntax propertyDeclarationSyntax: + { + var visit = walker.VisitPropertyDeclarationInSystem(propertyDeclarationSyntax); + if (visit.PropertyRequiresSourceGen) + indentedTextWriter.WriteLine(visit.SourceGeneratedProperty); + break; + } + } + } + } + + AddMiscellaneousMembers(indentedTextWriter, allDescriptionsForTheSameSystem); + AddEntityCommandBufferSystemFields(indentedTextWriter, allDescriptionsForTheSameSystem); + AddOnCreateForCompilerWithFields(indentedTextWriter, allDescriptionsForTheSameSystem, description.SystemType == SystemType.ISystem); - TypeDeclarationSyntax generatedPartialType; + for (int i = 0; i < result.NumNotClosedIfDirectives; i++) + indentedTextWriter.WriteLine("#endif"); + + indentedTextWriter.Indent--; + indentedTextWriter.WriteLine("}"); + + for (int i = 0; i < result.NumClosingBracesRequired; i++) + { + indentedTextWriter.Indent--; + indentedTextWriter.WriteLine("}"); + } + return (description.SyntaxTreeInfo, description.SystemTypeSyntax, indentedTextWriter.InnerWriter.ToString()); + } + + static void AppendBeginSystemType(IndentedTextWriter writer, TypeDeclarationSyntax originalSyntax) + { + writer.WriteLine("[global::System.Runtime.CompilerServices.CompilerGenerated]"); + writer.Write(originalSyntax.Modifiers.ToString()); switch (originalSyntax) { case StructDeclarationSyntax _: - generatedPartialType = SyntaxFactory.StructDeclaration(originalSyntax.Identifier); - allFullyQualifiedBaseTypeNames = allFullyQualifiedBaseTypeNames.Append("global::Unity.Entities.ISystemCompilerGenerated"); + writer.WriteLine(" struct {0}{1} : global::Unity.Entities.ISystemCompilerGenerated", originalSyntax.Identifier.ToString(), originalSyntax.TypeParameterList?.ToString()); break; default: - generatedPartialType = SyntaxFactory.ClassDeclaration(originalSyntax.Identifier); + writer.WriteLine(" class {0}{1}", originalSyntax.Identifier.ToString(), originalSyntax.TypeParameterList?.ToString()); break; } - - var baseTypeSyntaxNodes = allFullyQualifiedBaseTypeNames.Select(baseTypeName => SyntaxFactory.SimpleBaseType(SyntaxFactory.ParseTypeName(baseTypeName))); - var baseListSyntax = SyntaxFactory.BaseList().WithTypes(new SeparatedSyntaxList().AddRange(baseTypeSyntaxNodes)); - - generatedPartialType = - generatedPartialType - .WithBaseList(baseListSyntax) - .WithModifiers(originalSyntax.Modifiers) - .WithAttributeLists(GetCompilerGeneratedAttribute()); - - if (originalSyntax.TypeParameterList != null) - generatedPartialType = generatedPartialType.WithTypeParameterList(originalSyntax.TypeParameterList); - - return generatedPartialType; + writer.WriteLine("{"); + writer.Indent++; } - public static SyntaxTokenList RemovePartialModifier(SyntaxTokenList tokens) + private static void AddMiscellaneousMembers( + IndentedTextWriter writer, + SystemDescription[] descriptions) { - for (var i = 0; i < tokens.Count; i++) - { - if (tokens[i].IsKind(SyntaxKind.PartialKeyword)) - return tokens.RemoveAt(i); - } - - return tokens; + foreach (var desc in descriptions) + foreach (var mem in desc.NewMiscellaneousMembers) + mem.WriteTo(writer); } - private static TypeDeclarationSyntax ReplaceNodesInSystem(TypeDeclarationSyntax generatedPartialType, IReadOnlyList systemDescriptions) + private static void AddEntityCommandBufferSystemFields(IndentedTextWriter writer, SystemDescription[] descriptions) { - foreach (var description in systemDescriptions) - if (description.Rewriters.Any() || description.NonNestedReplacementsInMethods.Count != 0) - description.Rewriters.Insert(0,new CorrectionSystemReplacer(description.NonNestedReplacementsInMethods)); - - /* - Note: `GetTrackedSystems(systemDescriptions)` returns COPIES of the `TypeDeclarationSyntax` nodes we are tracking. - In fact, every single time we modify a node, we get back A COPY of the original node, rather than the original node itself. - This is because syntax nodes are immutable by nature. Whenever we track a node, it gets annotated with the string literal "Id" -- - i.e., we are in fact modifying the node. Thus, every time we track a node, we get back a copy of the original node with the new annotation applied. - */ - var trackedSystems = GetTrackedSystems(systemDescriptions).ToArray(); - var rewrittenMemberToSyntaxNode = new Dictionary(); - for (var i = 0; i < systemDescriptions.Count; i++) + var distinctEcbFields = new Dictionary(); + foreach (var desc in descriptions) { - var description = systemDescriptions[i]; - // Case hit when no candidates are found in all modules of the given system description. - if (!description.Rewriters.Any()) - continue; - var originalFilePath = description.SyntaxTreeInfo.Tree.FilePath.Replace('\\', '/'); - - SyntaxNode rewrittenSystem = trackedSystems[i]; - // Iterate through all rewriters. All rewriters will update their own `RewrittenMemberHashCodeToSyntaxNode` dictionaries during the rewrite process. - foreach (var rewriter in description.Rewriters) + foreach (var kvp in desc.FullEcbSystemTypeNamesToGeneratedFieldNames) { - if (rewriter is CorrectionSystemReplacer correctionSystemReplacer) - correctionSystemReplacer.SetOffsetLineNumber(description.SystemTypeSyntax.GetLineNumber()+1); - rewrittenSystem = rewriter.VisitTrackedSystem(rewrittenSystem, originalFilePath); - } - } - - // All rewriters have finished running - for (var index = 0; index < systemDescriptions.Count; index++) - { - var systemDescription = systemDescriptions[index]; - var annotationsToOriginalSyntaxNode = systemDescription.GetAnnotationsToOriginalSyntaxNodes(trackedSystems[index]); - - // As mentioned previously, all rewriters update their own `RewrittenMemberHashCodeToSyntaxNode` dictionaries during the rewrite process. - // By the time the next line is executed, all `RewrittenMemberHashCodeToSyntaxNode` dictionaries should contain all information about all rewritten nodes. - var systemRewriterRewrittenMemberAnnotationToSyntaxNode = new Dictionary(); - foreach (var rewriter in systemDescription.Rewriters) - foreach (var kvp in rewriter.RewrittenMemberAnnotationToSyntaxNode) - systemRewriterRewrittenMemberAnnotationToSyntaxNode[kvp.Key] = kvp.Value; - - foreach (var kvp in systemRewriterRewrittenMemberAnnotationToSyntaxNode) - { - var rewrittenMemberAnnotation = kvp.Key; - var rewrittenMemberSyntaxNode = kvp.Value; - - // If our rewriters have modified a new method or property, we need to ensure that the rewritten method/property has the right identifier name, attribute and modifiers. - switch (rewrittenMemberSyntaxNode) - { - case MethodDeclarationSyntax rewrittenMethod: - { - var originalMethodDeclarationSyntax = (MethodDeclarationSyntax)annotationsToOriginalSyntaxNode[rewrittenMethod.GetAnnotations(TrackedNodeAnnotationUsedByRoslyn).First()]; - var originalMethodSymbol = systemDescription.SemanticModel.GetDeclaredSymbol(originalMethodDeclarationSyntax); - - var targetMethodNameAndSignature = originalMethodSymbol.GetMethodAndParamsAsString(); - var stableHashCode = GetStableHashCode($"_{targetMethodNameAndSignature}") & 0x7fffffff; - var (modifiers, attributeList) = GetModifiersAndAttributes(targetMethodNameAndSignature, rewrittenMemberSyntaxNode, isMethod: true); - modifiers = RemovePartialModifier(modifiers); - - rewrittenMemberSyntaxNode = - rewrittenMethod - .WithExplicitInterfaceSpecifier(null) - .WithoutPreprocessorTrivia() - .WithIdentifier(SyntaxFactory.Identifier($"__{rewrittenMethod.Identifier.ValueText}_{stableHashCode:X}")) - .WithModifiers(modifiers) - .WithAttributeLists(attributeList); - break; - } - case PropertyDeclarationSyntax rewrittenProperty: - { - var originalPropertyDeclarationSyntaxNode = (PropertyDeclarationSyntax)annotationsToOriginalSyntaxNode[rewrittenProperty.GetAnnotations(TrackedNodeAnnotationUsedByRoslyn).First()]; - var originalPropertySymbol = systemDescription.SemanticModel.GetDeclaredSymbol(originalPropertyDeclarationSyntaxNode); - - var targetPropertyAndSignature = originalPropertySymbol.OriginalDefinition.ToString(); - var stableHashCode = GetStableHashCode($"_{targetPropertyAndSignature}") & 0x7fffffff; - var (modifiers, attributeList) = GetModifiersAndAttributes(targetPropertyAndSignature, rewrittenMemberSyntaxNode, isMethod: false); - modifiers = RemovePartialModifier(modifiers); - - rewrittenMemberSyntaxNode = - rewrittenProperty - .WithoutPreprocessorTrivia() - .WithIdentifier(SyntaxFactory.Identifier($"__{rewrittenProperty.Identifier.ValueText}_{stableHashCode:X}")) - .WithModifiers(modifiers) - .WithAttributeLists(attributeList); - break; - } - } - - rewrittenMemberToSyntaxNode[rewrittenMemberAnnotation] = rewrittenMemberSyntaxNode; + if (distinctEcbFields.ContainsKey(kvp.Key)) + continue; + distinctEcbFields.Add(kvp.Key, kvp.Value); + writer.WriteLine($"{kvp.Key} {kvp.Value};"); } } - - return generatedPartialType.WithMembers(new SyntaxList(rewrittenMemberToSyntaxNode.Values)); } - - static IEnumerable GetTrackedSystems(IEnumerable descriptions) + private static void AddOnCreateForCompilerWithFields(IndentedTextWriter writer, SystemDescription[] descriptions, bool isISystem) { - foreach (var description in descriptions) + // Only needs to create OnCreateForCompiler in SystemBase if members are present + if (!descriptions.Any(d => + d.QueriesAndHandles.TypeHandleStructNestedFields.Count > 0 || + d.QueriesAndHandles.QueryFieldsToFieldNames.Count > 0 || + d.AdditionalStatementsInOnCreateForCompilerMethod.Count > 0)) { - // `TrackNodes()` returns a new copy of the system, in which tracked nodes are annotated with the string literal "Id". - var trackedSystem = - description.SystemTypeSyntax.TrackNodes( - description.Rewriters - .SelectMany(rewriter => rewriter.NodesToTrack) - .Concat(description.SystemTypeSyntax.Members)); - - yield return trackedSystem; + if (isISystem) + writer.WriteLine("public void OnCreateForCompiler(ref SystemState state){}"); + return; } - } - static (SyntaxTokenList, SyntaxList) GetModifiersAndAttributes(string targetMethodNameAndSignature, MemberDeclarationSyntax rewritten, bool isMethod) - { - // Make sure emit new method as private so that containing type can be sealed - var modifiers = new SyntaxTokenList(rewritten.Modifiers.Where( - m => !(m.IsKind(SyntaxKind.OverrideKeyword) || m.IsKind(SyntaxKind.PublicKeyword) || m.IsKind(SyntaxKind.ProtectedKeyword) || - m.IsKind(SyntaxKind.VirtualKeyword) || m.IsKind(SyntaxKind.AbstractKeyword)))); + QueriesAndHandles.WriteTypeHandleStructAndAssignQueriesMethod(writer, descriptions.Select(d => d.QueriesAndHandles).ToArray()); - var dotsCompilerPatchedArguments = SyntaxFactory.ParseAttributeArgumentList($"(\"{targetMethodNameAndSignature}\")"); - var attributeName = isMethod ? "global::Unity.Entities.DOTSCompilerPatchedMethod" : "global::Unity.Entities.DOTSCompilerPatchedProperty"; + var additionalStatementsInOnCreate = new HashSet(); + foreach (var description in descriptions) + foreach (var statement in description.AdditionalStatementsInOnCreateForCompilerMethod) + additionalStatementsInOnCreate.Add(statement); - var dotsCompilerPatchedMethodAttribute = SyntaxFactory.Attribute(SyntaxFactory.IdentifierName(attributeName), dotsCompilerPatchedArguments); - return (modifiers, new SyntaxList(SyntaxFactory.AttributeList(SyntaxFactory.SeparatedList(new[] {dotsCompilerPatchedMethodAttribute})))); + if (isISystem) + { + writer.WriteLine("public void OnCreateForCompiler(ref SystemState state)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("__AssignQueries(ref state);"); + writer.WriteLine("__TypeHandle.__AssignHandles(ref state);"); + foreach (var statement in additionalStatementsInOnCreate) + writer.WriteLine(statement); + writer.Indent--; + writer.WriteLine("}"); + } + else + { + writer.WriteLine("protected override void OnCreateForCompiler()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("base.OnCreateForCompiler();"); + writer.WriteLine("__AssignQueries(ref this.CheckedStateRef);"); + writer.WriteLine("__TypeHandle.__AssignHandles(ref this.CheckedStateRef);"); + foreach (var statement in additionalStatementsInOnCreate) + writer.WriteLine(statement); + writer.Indent--; + writer.WriteLine("}"); + } } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/PreprocessorInfo.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/PreprocessorInfo.cs new file mode 100644 index 0000000..df49533 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/PreprocessorInfo.cs @@ -0,0 +1,64 @@ +using System.Collections.Generic; + +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public struct PreprocessorInfo +{ + public bool IsForDotsRuntime { get; private set; } + public bool IsDotsRuntimeProfilerEnabled { get; private set; } + public bool IsProfilerEnabled { get; private set; } + public bool IsDotsDebugMode { get; private set; } + public bool IsUnityCollectionChecksEnabled { get; private set; } + public bool IsForUnityEditor { get; private set; } + public bool IsDevelopmentBuildEnabled { get; private set; } + + public static PreprocessorInfo From(IEnumerable preprocessorSymbolNames) + { + bool isUnityCollectionChecksEnabled = false; + bool isForDotsRuntime = false; + bool isDotsRuntimeProfilerEnabled = false; + bool isProfilerEnabled = false; + bool isDotsDebugMode = false; + bool isForUnityEditor = false; + bool isDevelopmentBuildEnabled = false; + + foreach (var name in preprocessorSymbolNames) + { + switch (name) + { + case "DEVELOPMENT_BUILD": + isDevelopmentBuildEnabled = true; + break; + case "UNITY_EDITOR": + isForUnityEditor = true; + break; + case "ENABLE_UNITY_COLLECTIONS_CHECKS": + isUnityCollectionChecksEnabled = true; + break; + case "UNITY_DOTSRUNTIME": + isForDotsRuntime = true; + break; + case "ENABLE_DOTSRUNTIME_PROFILER": + isDotsRuntimeProfilerEnabled = true; + break; + case "ENABLE_PROFILER": + isProfilerEnabled = true; + break; + case "UNITY_DOTS_DEBUG": + isDotsDebugMode = true; + break; + } + } + + return new PreprocessorInfo + { + IsProfilerEnabled = isProfilerEnabled, + IsDotsRuntimeProfilerEnabled = isDotsRuntimeProfilerEnabled, + IsForDotsRuntime = isForDotsRuntime, + IsDotsDebugMode = isDotsDebugMode, + IsUnityCollectionChecksEnabled = isUnityCollectionChecksEnabled, + IsForUnityEditor = isForUnityEditor, + IsDevelopmentBuildEnabled = isDevelopmentBuildEnabled + }; + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueriesAndHandles.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueriesAndHandles.cs new file mode 100644 index 0000000..1fc1cbc --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueriesAndHandles.cs @@ -0,0 +1,245 @@ +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public readonly struct QueriesAndHandles +{ + public readonly HashSet TypeHandleStructNestedFields; + public readonly Dictionary QueryFieldsToFieldNames; + public readonly int UniqueId; + + QueriesAndHandles(HashSet typeHandleStructNestedFields, Dictionary queryFieldsToFieldNames, SyntaxNode owningType) + { + TypeHandleStructNestedFields = typeHandleStructNestedFields; + QueryFieldsToFieldNames = queryFieldsToFieldNames; + + var systemLineSpan = owningType.GetLocation().GetLineSpan(); + UniqueId = (systemLineSpan.Span.Start.Line + SourceGenHelpers.GetStableHashCode(Path.GetFileName(systemLineSpan.Path))) & 0x7fffffff; + } + + public static QueriesAndHandles Create(TypeDeclarationSyntax owningType) + => new(new HashSet(),new Dictionary(), owningType); + + public string GetOrCreateQueryField(IQueryFieldDescription queryFieldDescription, string generatedName = null) + { + if (QueryFieldsToFieldNames.TryGetValue(queryFieldDescription, out string matchingFieldName)) + return matchingFieldName; + generatedName ??= $"__query_{UniqueId}_{QueryFieldsToFieldNames.Count}"; + QueryFieldsToFieldNames.Add(queryFieldDescription, generatedName); + return generatedName; + } + + // We cannot call `GetOrCreateTypeHandleField(ITypeSymbol typeSymbol, bool isReadOnly)` when creating type handle + // fields for source-generated types, because there aren't any type symbols available yet. + public string GetOrCreateSourceGeneratedTypeHandleField(string containerTypeFullName) + { + var description = new ContainerTypeHandleFieldDescription(containerTypeFullName); + TypeHandleStructNestedFields.Add(description); + + return description.GeneratedFieldName; + } + + public string GetOrCreateAspectLookup(ITypeSymbol entityTypeLookup, bool isReadOnly) + { + var entityTypeLookupField = new AspectLookupFieldDescription(entityTypeLookup, isReadOnly); + TypeHandleStructNestedFields.Add(entityTypeLookupField); + + return entityTypeLookupField.GeneratedFieldName; + } + + public string GetOrCreateEntityTypeHandleField() + { + var entityTypeHandleFieldDescription = new EntityTypeHandleFieldDescription(); + TypeHandleStructNestedFields.Add(entityTypeHandleFieldDescription); + + return entityTypeHandleFieldDescription.GeneratedFieldName; + } + + public string GetOrCreateTypeHandleField(ITypeSymbol typeSymbol, bool isReadOnly) + { + var typeHandleFieldDescription = new TypeHandleFieldDescription(typeSymbol, isReadOnly); + TypeHandleStructNestedFields.Add(typeHandleFieldDescription); + + return typeHandleFieldDescription.GeneratedFieldName; + } + + public string GetOrCreateComponentLookupField(ITypeSymbol typeSymbol, bool isReadOnly) + { + var lookupField = new ComponentLookupFieldDescription(typeSymbol, isReadOnly); + TypeHandleStructNestedFields.Add(lookupField); + + return lookupField.GeneratedFieldName; + } + public string GetOrCreateJobEntityHandle(ITypeSymbol typeSymbol, bool assignDefaultQuery) + { + var lookupField = new JobEntityQueryAndHandleDescription(typeSymbol, assignDefaultQuery); + TypeHandleStructNestedFields.Add(lookupField); + return lookupField.GeneratedFieldName; + } + + public string GetOrCreateBufferLookupField(ITypeSymbol typeSymbol, bool isReadOnly) + { + var bufferLookupField = new BufferLookupFieldDescription(typeSymbol, isReadOnly); + TypeHandleStructNestedFields.Add(bufferLookupField); + + return bufferLookupField.GeneratedFieldName; + } + + public string GetOrCreateEntityStorageInfoLookupField() + { + var storageLookupField = new EntityStorageInfoLookupFieldDescription(); + TypeHandleStructNestedFields.Add(storageLookupField); + + return storageLookupField.GeneratedFieldName; + } + + public static void WriteTypeHandleStructAndAssignQueriesMethod(IndentedTextWriter writer, params QueriesAndHandles[] handleDescriptions) + { + if (handleDescriptions.Length == 0) + throw new InvalidOperationException("Didn't pass in any handle Descriptions"); + + writer.WriteLine(); + writer.WriteLine("TypeHandle __TypeHandle;"); + + var allQueryFieldsToGenerate = handleDescriptions.SelectMany(d => d.QueryFieldsToFieldNames).ToArray(); + foreach (var kvp in allQueryFieldsToGenerate) + writer.WriteLine(kvp.Key.GetFieldDeclaration(kvp.Value)); + + writer.WriteLine("struct TypeHandle"); + writer.WriteLine("{"); + writer.Indent++; + + // This handles the aliasing of lookups and typehandles + var nonQueryFields = handleDescriptions[0].TypeHandleStructNestedFields; + for (var i = 1; i < handleDescriptions.Length; i++) + { + var desc = handleDescriptions[i]; + foreach (var nonQueryField in desc.TypeHandleStructNestedFields) + nonQueryFields.Add(nonQueryField); + } + + foreach (var memberDescription in nonQueryFields) + memberDescription.AppendMemberDeclaration(writer, forcePublic: true); + + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine("public void __AssignHandles(ref global::Unity.Entities.SystemState state)"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var nonQueryField in nonQueryFields) + writer.WriteLine(nonQueryField.GetMemberAssignment()); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine("void __AssignQueries(ref global::Unity.Entities.SystemState state)"); + writer.WriteLine("{"); + writer.Indent++; + + writer.WriteLine("var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp);"); + foreach (var kvp in allQueryFieldsToGenerate) + kvp.Key.WriteEntityQueryFieldAssignment(writer, kvp.Value); + + writer.WriteLine("entityQueryBuilder.Dispose();"); + + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + } + + // Expects the first handle description to be the one where the TypeHandle is generated. + // By the point this is called all HandleDescriptions should have been filled out! + public static void WriteInternalCompilerQueryAndHandleDataStruct( + IndentedTextWriter writer, + bool shouldHaveUpdate, + bool fieldsArePublic, + Action writeAdditionalSyntaxInInternalCompilerQueryAndHandleData, + params QueriesAndHandles[] handlesAndQueries) + { + if (handlesAndQueries.Length == 0) + throw new InvalidOperationException("Didn't pass in any handle Descriptions"); + + writer.WriteLine("/// Used internally by the compiler, we won't promise this exists in the future "); + writer.WriteLine("public struct InternalCompilerQueryAndHandleData"); + writer.WriteLine("{"); + writer.Indent++; + if (fieldsArePublic) + writer.Write("public "); + writer.WriteLine("TypeHandle __TypeHandle;"); + + var allQueryFieldsToGenerate = handlesAndQueries.SelectMany(d => d.QueryFieldsToFieldNames).ToArray(); + foreach (var kvp in allQueryFieldsToGenerate) + writer.WriteLine(kvp.Key.GetFieldDeclaration(kvp.Value, fieldsArePublic)); + + if (fieldsArePublic) + writer.Write("public "); + writer.WriteLine("struct TypeHandle"); + writer.WriteLine("{"); + writer.Indent++; + + // This handles the aliasing of lookups and typehandles + var nonQueryFields = handlesAndQueries[0].TypeHandleStructNestedFields; + for (var i = 1; i < handlesAndQueries.Length; i++) + { + var desc = handlesAndQueries[i]; + foreach (var nonQueryField in desc.TypeHandleStructNestedFields) + nonQueryFields.Add(nonQueryField); + } + foreach (var nonQueryField in nonQueryFields) + nonQueryField.AppendMemberDeclaration(writer, fieldsArePublic); + + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine("public void __AssignHandles(ref global::Unity.Entities.SystemState state)"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var nonQueryField in nonQueryFields) + writer.WriteLine(nonQueryField.GetMemberAssignment()); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + + if (shouldHaveUpdate) + { + writer.WriteLine("public void Update(ref global::Unity.Entities.SystemState state)"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var nonQueryField in nonQueryFields) + writer.WriteLine($"{nonQueryField.GeneratedFieldName}.Update(ref state);"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + } + + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine("void __AssignQueries(ref global::Unity.Entities.SystemState state)"); + writer.WriteLine("{"); + writer.Indent++; + + writer.WriteLine("var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp);"); + foreach (var kvp in allQueryFieldsToGenerate) + kvp.Key.WriteEntityQueryFieldAssignment(writer, kvp.Value); + + writer.WriteLine("entityQueryBuilder.Dispose();"); + + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + + writeAdditionalSyntaxInInternalCompilerQueryAndHandleData?.Invoke(writer); + + writer.Indent--; + writer.WriteLine("}"); + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueryConstructionErrors.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueryConstructionErrors.cs index 44e40e6..79e4172 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueryConstructionErrors.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueryConstructionErrors.cs @@ -1,7 +1,7 @@ using Microsoft.CodeAnalysis; -using Unity.Entities.SourceGen.SystemGenerator.Common; +using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common { public static class QueryConstructionErrors { @@ -47,5 +47,13 @@ public static void SGQC004(SystemDescription systemDescription, Location locatio $"You specified the same component type ({componentTypeFullName}) in both {queryGroup1Name} and {queryGroup2Name}, which are mutually exclusive.", location); } + + public static void SGQC005(SystemDescription systemDescription, Location location, string argumentTypeName, string invokedMethodName) + { + systemDescription.LogError(nameof(SGQC005), + ErrorTitle, + $"{invokedMethodName}<{argumentTypeName}>() is not supported as {invokedMethodName}'s type argument can not itself be a generic type parameter.", + location); + } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueryVerification.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueryVerification.cs index 42387c1..7795d93 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueryVerification.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/QueryVerification.cs @@ -1,9 +1,9 @@ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; -using Unity.Entities.SourceGen.SystemGenerator.Common; +using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common { public static class QueryVerification { @@ -19,18 +19,26 @@ public static bool VerifyQueryTypeCorrectness( { var queryTypeSymbol = query.TypeSymbol; - bool isValidQueryType = - queryTypeSymbol.InheritsFromInterface("Unity.Entities.IComponentData") || - queryTypeSymbol.InheritsFromInterface("Unity.Entities.ISharedComponentData") || - queryTypeSymbol.InheritsFromInterface("Unity.Entities.IBufferElementData") || - queryTypeSymbol.InheritsFromInterface($"Unity.Entities.IAspect") || - queryTypeSymbol.Is("UnityEngine.Object"); - - if (!isValidQueryType) + if (queryTypeSymbol is ITypeParameterSymbol) { - QueryConstructionErrors.SGQC001(systemDescription, location, queryTypeSymbol.Name, invokedMethodName); + QueryConstructionErrors.SGQC005(systemDescription, location, queryTypeSymbol.Name, invokedMethodName); success = false; } + else + { + var isValidQueryType = + queryTypeSymbol.InheritsFromInterface("Unity.Entities.IComponentData") || + queryTypeSymbol.InheritsFromInterface("Unity.Entities.ISharedComponentData") || + queryTypeSymbol.InheritsFromInterface("Unity.Entities.IBufferElementData") || + queryTypeSymbol.InheritsFromInterface($"Unity.Entities.IAspect") || + queryTypeSymbol.Is("UnityEngine.Object"); + + if (!isValidQueryType) + { + QueryConstructionErrors.SGQC001(systemDescription, location, queryTypeSymbol.Name, invokedMethodName); + success = false; + } + } } return success; } @@ -74,16 +82,8 @@ public static bool VerifyNoMutuallyExclusiveQueries( string queryGroup2Name, bool compareTypeSymbolsOnly = false) { - ITypeSymbol QueryToTypeSymbol(Query query) - { - if (query.TypeSymbol.ToFullName().StartsWith("global::Unity.Entities.Ref") && - ((INamedTypeSymbol)query.TypeSymbol).TypeArguments.Length > 0) - return ((INamedTypeSymbol)query.TypeSymbol).TypeArguments.First(); - else - return query.TypeSymbol; - } - var mutuallyExclusiveQueryTypes1 = mutuallyExclusiveQueryGroup1.Select(QueryToTypeSymbol); - var mutuallyExclusiveQueryTypes2 = mutuallyExclusiveQueryGroup2.Select(QueryToTypeSymbol); + var mutuallyExclusiveQueryTypes1 = mutuallyExclusiveQueryGroup1.Select(q => q.TypeSymbol); + var mutuallyExclusiveQueryTypes2 = mutuallyExclusiveQueryGroup2.Select(q => q.TypeSymbol); if (compareTypeSymbolsOnly) { diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SingleArchetypeQueryFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SingleArchetypeQueryFieldDescription.cs index baeb001..a955c65 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SingleArchetypeQueryFieldDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SingleArchetypeQueryFieldDescription.cs @@ -1,4 +1,5 @@ using System; +using System.CodeDom.Compiler; using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; @@ -9,15 +10,15 @@ namespace Unity.Entities.SourceGen.SystemGenerator.Common public readonly struct SingleArchetypeQueryFieldDescription : IEquatable, IQueryFieldDescription { readonly Archetype _archetype; - readonly IReadOnlyCollection _changeFilterTypes; + readonly IReadOnlyList _changeFilterTypes; readonly string _queryStorageFieldName; public string GetFieldDeclaration(string generatedQueryFieldName, bool forcePublic = false) - => $"{(forcePublic ? "public" : "")} global::Unity.Entities.EntityQuery {generatedQueryFieldName};"; + => $"{(forcePublic ? "public " : "")}global::Unity.Entities.EntityQuery {generatedQueryFieldName};"; public SingleArchetypeQueryFieldDescription( Archetype archetype, - IReadOnlyCollection changeFilterTypes = null, + IReadOnlyList changeFilterTypes = null, string queryStorageFieldName = null) { _archetype = archetype; @@ -57,128 +58,116 @@ public override int GetHashCode() public static bool operator ==(SingleArchetypeQueryFieldDescription left, SingleArchetypeQueryFieldDescription right) => Equals(left, right); public static bool operator !=(SingleArchetypeQueryFieldDescription left, SingleArchetypeQueryFieldDescription right) => !Equals(left, right); - /// - /// Use EntityQueryBuilder to create the query - /// Support creating queries with aspects - /// - /// - /// - /// - string EntityQueryWithEntityQueryBuilder(string generatedQueryFieldName) + static (HashSet readOnlyTypeNames, HashSet readWriteTypeNames) GetDistinctRequiredTypeNames(IEnumerable presentTypes) { - var code = new System.Text.StringBuilder(); - var codeAspect = new System.Text.StringBuilder(); - code.AppendLine($@"{generatedQueryFieldName} = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp)"); - foreach (var comp in _archetype.All.Concat(_changeFilterTypes)) - if (comp.TypeSymbol.IsAspect()) - codeAspect.AppendLine($".WithAspect<{comp.TypeSymbol.ToFullName()}>()"); - else - code.AppendLine($@".WithAll{(comp.IsReadOnly ? "" : "RW")}<{comp.TypeSymbol.ToFullName()}>()"); - foreach (var comp in _archetype.Any) - code.AppendLine($@".WithAny<{comp.TypeSymbol.ToFullName()}>()"); - foreach (var comp in _archetype.None) - code.AppendLine($@".WithNone<{comp.TypeSymbol.ToFullName()}>()"); - foreach (var comp in _archetype.Disabled) - code.AppendLine($@".WithDisabled<{comp.TypeSymbol.ToFullName()}>()"); - foreach (var comp in _archetype.Absent) - code.AppendLine($@".WithAbsent<{comp.TypeSymbol.ToFullName()}>()"); + var readOnlyTypeNames = new HashSet(); + var readWriteTypeNames = new HashSet(); - // Append all ".WithAspect" calls. They must be done after all "WithAll", "WithAny" and "WithNone" calls to avoid component aliasing - code.Append(codeAspect); + foreach (var type in presentTypes) + AddDistinctQueryType(type, type.IsReadOnly); - if(_archetype.Options != EntityQueryOptions.Default) - code.Append($".WithOptions({_archetype.Options.GetFlags().Select(flag => $"global::Unity.Entities.EntityQueryOptions.{flag.ToString()}").SeparateByBinaryOr()})"); + return (readOnlyTypeNames, readWriteTypeNames); - code.AppendLine($".Build(ref state);"); - return code.ToString(); + void AddDistinctQueryType(Query q, bool isReadOnly) + { + var queryTypeFullName = q.TypeSymbol.ToFullName(); + if (!isReadOnly) + { + readOnlyTypeNames.Remove(queryTypeFullName); + readWriteTypeNames.Add(queryTypeFullName); + } + else if (!readWriteTypeNames.Contains(queryTypeFullName)) + readOnlyTypeNames.Add(queryTypeFullName); + } } - public string EntityQueryFieldAssignment(string generatedQueryFieldName) + public void WriteEntityQueryFieldAssignment(IndentedTextWriter writer, string generatedQueryFieldName) { - // if there are any aspects in our query, use the EntityQueryBuilder to create the query - if (_archetype.All.Concat(_changeFilterTypes).Any(x => x.TypeSymbol.IsAspect())) - return EntityQueryWithEntityQueryBuilder(generatedQueryFieldName); - - var entityQuerySetup = - $@"{generatedQueryFieldName} = state.GetEntityQuery - ( - new global::Unity.Entities.EntityQueryDesc - {{ - All = {DistinctQueryTypesFor()}, - Any = new global::Unity.Entities.ComponentType[] {{ - {_archetype.Any.Select(q => q.ToString()).Distinct().SeparateByCommaAndNewLine()} - }}, - None = new global::Unity.Entities.ComponentType[] {{ - {_archetype.None.Select(q => q.ToString()).Distinct().SeparateByCommaAndNewLine()} - }}, - Disabled = new global::Unity.Entities.ComponentType[] {{ - {_archetype.Disabled.Select(q => q.ToString()).Distinct().SeparateByCommaAndNewLine()} - }}, - Absent = new global::Unity.Entities.ComponentType[] {{ - {_archetype.Absent.Select(q => q.ToString()).Distinct().SeparateByCommaAndNewLine()} - }}, - Options = - {_archetype.Options.GetFlags().Select(flag => $"global::Unity.Entities.EntityQueryOptions.{flag.ToString()}").SeparateByBinaryOr()} - }} - );"; - if (_queryStorageFieldName != null) - entityQuerySetup = $"{_queryStorageFieldName} = " + entityQuerySetup; + writer.WriteLine($"{_queryStorageFieldName} = "); - if (_changeFilterTypes.Any()) + var codeAspect = new List(8); + writer.WriteLine($"{generatedQueryFieldName} = "); + writer.Indent++; + writer.WriteLine("entityQueryBuilder"); + writer.Indent++; + + var requiredTypes = _archetype.All.Concat(_changeFilterTypes); + var presentComponentTypes = new List(capacity: 8); + + foreach (var comp in requiredTypes) + if (comp.TypeSymbol.IsAspect()) + codeAspect.Add($".WithAspect<{comp.TypeSymbol.ToFullName()}>()"); + else + presentComponentTypes.Add(comp); + + foreach (var comp in _archetype.Any) + { + writer.WriteLine(comp.IsReadOnly + ? $".WithAny<{comp.TypeSymbol.ToFullName()}>()" + : $".WithAnyRW<{comp.TypeSymbol.ToFullName()}>()"); + } + foreach (var comp in _archetype.None) { - entityQuerySetup += - $@"{generatedQueryFieldName}.SetChangedVersionFilter(new ComponentType[{_changeFilterTypes.Count}] - {{ - {_changeFilterTypes.Select(q => q.ToString()).SeparateByComma()} - }});"; + writer.WriteLine(comp.IsReadOnly + ? $".WithNone<{comp.TypeSymbol.ToFullName()}>()" + // We support specifying an enableable type as a `None` component *and* as an iterable query type in the same `SystemAPI.Query` invocation. + // Users may write e.g. `SystemAPI.Query>().WithNone()` in order to specifically iterate through + // *disabled* `MyEnableableComponent`s. In that case, `MyEnableableComponent` is labelled a `None` component with read-write access, since `EnabledRefRW` + // usages require read-write access. Since `.WithNoneRW` is not available as part of the `EntityQueryBuilder` public API, we can use `.WithDisabledRW` instead. + : $".WithDisabledRW<{comp.TypeSymbol.ToFullName()}>()"); } + foreach (var comp in _archetype.Disabled) + { + writer.WriteLine(comp.IsReadOnly + ? $".WithDisabled<{comp.TypeSymbol.ToFullName()}>()" + : $".WithDisabledRW<{comp.TypeSymbol.ToFullName()}>()"); + } + foreach (var comp in _archetype.Present) + { + writer.WriteLine(comp.IsReadOnly + ? $".WithPresent<{comp.TypeSymbol.ToFullName()}>()" + : $".WithPresentRW<{comp.TypeSymbol.ToFullName()}>()"); + } + foreach (var comp in _archetype.Absent) + writer.WriteLine($".WithAbsent<{comp.TypeSymbol.ToFullName()}>()"); - return entityQuerySetup; - } + var distinctPresentTypeNames = GetDistinctRequiredTypeNames(presentComponentTypes); + foreach (var ro in distinctPresentTypeNames.readOnlyTypeNames) + writer.WriteLine($".WithAll<{ro}>()"); - string DistinctQueryTypesFor() - { - var readOnlyTypeNames = new HashSet(); - var readWriteTypeNames = new HashSet(); + foreach (var ro in distinctPresentTypeNames.readWriteTypeNames) + writer.WriteLine($".WithAllRW<{ro}>()"); - int componentCount = 0; + // Append all ".WithAspect" calls. They must be done after all "WithAll", "WithAny" and "WithNone" calls to avoid component aliasing + foreach (var code in codeAspect) + writer.WriteLine(code); - void AddQueryType(ITypeSymbol queryType, bool isReadOnly) - { - if (queryType == null) - return; + if(_archetype.Options != EntityQueryOptions.Default) + writer.WriteLine($".WithOptions({_archetype.Options.GetAsFlagStringSeperatedByOr()})"); - var queryTypeFullName = queryType.ToFullName(); - ++componentCount; - if (!isReadOnly) - { - readOnlyTypeNames.Remove(queryTypeFullName); - readWriteTypeNames.Add(queryTypeFullName); - } - else - { - if (!readWriteTypeNames.Contains(queryTypeFullName) && - !readOnlyTypeNames.Contains(queryTypeFullName)) - { - readOnlyTypeNames.Add(queryTypeFullName); - } - } - } + writer.WriteLine(".Build(ref state);"); + writer.Indent--; + writer.Indent--; + writer.WriteLine("entityQueryBuilder.Reset();"); - foreach (var allComponentType in _archetype.All) - AddQueryType(allComponentType.TypeSymbol, allComponentType.IsReadOnly); + if (_changeFilterTypes.Any()) + { + writer.WriteLine($@"{generatedQueryFieldName}.SetChangedVersionFilter(new ComponentType[{_changeFilterTypes.Count}]"); + writer.WriteLine("{"); + writer.Indent++; - foreach (var changeFilterType in _changeFilterTypes) - AddQueryType(changeFilterType.TypeSymbol, changeFilterType.IsReadOnly); + for (var index = 0; index < _changeFilterTypes.Count; index++) + { + writer.WriteLine($"new ComponentType(typeof({_changeFilterTypes[index].TypeSymbol.ToFullName()}))"); - if (componentCount == 0) - return "new global::Unity.Entities.ComponentType[]{}"; + if (index < _changeFilterTypes.Count - 1) + writer.WriteLine(","); + } - var eComponents = readOnlyTypeNames - .Select(type => $@"global::Unity.Entities.ComponentType.ReadOnly<{type}>()") - .Concat(readWriteTypeNames.Select(type => $@"global::Unity.Entities.ComponentType.ReadWrite<{type}>()")); - return $"new global::Unity.Entities.ComponentType[] {{{eComponents.Distinct().SeparateByCommaAndNewLine()}}}"; + writer.Indent--; + writer.WriteLine("});"); + } } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemDescription.cs index 2d38bac..29e5a2d 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemDescription.cs @@ -1,147 +1,150 @@ using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.Common +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public readonly struct SystemDescription : ISourceGeneratorDiagnosable { - public enum SystemType + public List SourceGenDiagnostics { get; } + + public readonly QueriesAndHandles QueriesAndHandles; + public readonly TypeDeclarationSyntax SystemTypeSyntax; + public readonly SystemType SystemType; + public readonly SemanticModel SemanticModel; + public readonly INamedTypeSymbol SystemTypeSymbol; + public readonly string SystemTypeFullName; + public readonly SyntaxTreeInfo SyntaxTreeInfo; + public readonly Dictionary SyntaxWalkers; + public readonly Dictionary CandidateNodes; + public readonly Dictionary FullEcbSystemTypeNamesToGeneratedFieldNames; + public readonly HashSet AdditionalStatementsInOnCreateForCompilerMethod; + public readonly List NewMiscellaneousMembers; + public readonly PreprocessorInfo PreprocessorInfo; + + public SystemDescription( + TypeDeclarationSyntax systemTypeSyntax, + SystemType systemType, + INamedTypeSymbol systemTypeSymbol, + SemanticModel semanticModel, + SyntaxTreeInfo syntaxTreeInfo, + Dictionary candidateNodes, + PreprocessorInfo preprocessorInfo) { - Unknown, - SystemBase, - ISystem + SystemTypeSyntax = systemTypeSyntax; + SemanticModel = semanticModel; + SystemTypeSymbol = systemTypeSymbol; + SystemType = systemType; + SystemTypeFullName = SystemTypeSymbol.ToFullName(); + SyntaxTreeInfo = syntaxTreeInfo; + SourceGenDiagnostics = new List(); + NewMiscellaneousMembers = new List(); + AdditionalStatementsInOnCreateForCompilerMethod = new HashSet(); + FullEcbSystemTypeNamesToGeneratedFieldNames = new Dictionary(); + QueriesAndHandles = QueriesAndHandles.Create(systemTypeSyntax); + CandidateNodes = candidateNodes ?? new Dictionary(); + SyntaxWalkers = new Dictionary(); + PreprocessorInfo = preprocessorInfo; } - public static class SystemTypeHelpers + public bool ContainsChangesToSystem() => + QueriesAndHandles.QueryFieldsToFieldNames.Any() + || QueriesAndHandles.TypeHandleStructNestedFields.Any() + || FullEcbSystemTypeNamesToGeneratedFieldNames.Any() + || SyntaxWalkers.Any() + || NewMiscellaneousMembers.Any(); + + public string GetOrCreateEntityCommandBufferSystemField(ITypeSymbol ecbSystemTypeSymbol) { - public static (bool IsSystemType, SystemType SystemType) TryGetSystemType(this ITypeSymbol namedSystemTypeSymbol) + string fullEcbSystemTypeName = ecbSystemTypeSymbol.ToFullName(); + + if (FullEcbSystemTypeNamesToGeneratedFieldNames.TryGetValue(fullEcbSystemTypeName, out var generatedFieldName)) + return generatedFieldName; + + generatedFieldName = $"__{ecbSystemTypeSymbol.ToValidIdentifier()}"; + FullEcbSystemTypeNamesToGeneratedFieldNames[fullEcbSystemTypeName] = generatedFieldName; + + return generatedFieldName; + } + + public string OriginalFilePath => SyntaxTreeInfo.Tree.FilePath.Replace('\\', '/'); + + public HashSet GetStatementsRequiringLineDirectives() + { + var statements = new HashSet(); + + foreach (var node in CandidateNodes.Keys) { - if (namedSystemTypeSymbol.Is("Unity.Entities.SystemBase")) - return (true, SystemType.SystemBase); - if (namedSystemTypeSymbol.InheritsFromInterface("Unity.Entities.ISystem")) - return (true, SystemType.ISystem); - return (false, default); + var containingMember = node.AncestorOfKind(); + if (containingMember is MethodDeclarationSyntax { Body: not null } methodDeclarationSyntax) + foreach (var statement in methodDeclarationSyntax.Body.DescendantNodes().OfType()) + statements.Add(statement); } + + return statements; } - public readonly struct SystemDescription : ISourceGeneratorDiagnosable, IAdditionalHandlesInfo + public Dictionary> CandidateNodesGroupedByMethodOrProperty { - public List Diagnostics { get; } - - public HandlesDescription HandlesDescription { get; } - - public TypeDeclarationSyntax TypeSyntax => SystemTypeSyntax; - - public SystemType SystemType { get; } - public readonly INamedTypeSymbol SystemTypeSymbol; - public readonly TypeDeclarationSyntax SystemTypeSyntax; - public SemanticModel SemanticModel { get; } - public readonly IReadOnlyCollection PreprocessorSymbolNames; - public readonly string SystemTypeFullName; - public readonly SyntaxTreeInfo SyntaxTreeInfo; - public readonly IReadOnlyCollection FullyQualifiedBaseTypeNames; - public readonly List Rewriters; - public readonly Dictionary NonNestedReplacementsInMethods; - public readonly List NewMiscellaneousMembers; - public readonly Dictionary FullEcbSystemTypeNamesToGeneratedFieldNames; - public readonly HashSet AdditionalStatementsInOnCreateForCompilerMethod; - - public readonly bool IsForDotsRuntime; - public readonly bool IsDotsRuntimeProfilerEnabled; - public readonly bool IsProfilerEnabled; - public readonly bool IsDotsDebugMode; - public readonly bool IsUnityCollectionChecksEnabled; - - public SystemDescription( - TypeDeclarationSyntax originalSystemTypeSyntax, - SystemType systemType, - INamedTypeSymbol systemTypeSymbol, - SemanticModel semanticModel, - IEnumerable preprocessorSymbolNames, - SyntaxTreeInfo syntaxTreeInfo) + get { - SystemTypeSyntax = originalSystemTypeSyntax; - SemanticModel = semanticModel; - PreprocessorSymbolNames = preprocessorSymbolNames.ToArray(); - SystemTypeSymbol = systemTypeSymbol; - SystemType = systemType; - SystemTypeFullName = SystemTypeSymbol.ToFullName(); - SyntaxTreeInfo = syntaxTreeInfo; - FullyQualifiedBaseTypeNames = SystemTypeSymbol.GetAllFullyQualifiedInterfaceAndBaseTypeNames().ToArray(); - Diagnostics = new List(); - NewMiscellaneousMembers = new List(); - NonNestedReplacementsInMethods = new Dictionary(); - AdditionalStatementsInOnCreateForCompilerMethod = new HashSet(); - FullEcbSystemTypeNamesToGeneratedFieldNames = new Dictionary(); - HandlesDescription = HandlesDescription.Create(originalSystemTypeSyntax); - Rewriters = new List(); - - IsUnityCollectionChecksEnabled = false; - IsForDotsRuntime = false; - IsDotsRuntimeProfilerEnabled = false; - IsProfilerEnabled = false; - IsDotsDebugMode = false; - - foreach (var name in PreprocessorSymbolNames) + var candidateNodesGroupedByContainingMember = new Dictionary>(); + foreach (var kvp in CandidateNodes) { - switch (name) - { - case "ENABLE_UNITY_COLLECTIONS_CHECKS": - IsUnityCollectionChecksEnabled = true; - break; - case "UNITY_DOTSRUNTIME": - IsForDotsRuntime = true; - break; - case "ENABLE_DOTSRUNTIME_PROFILER": - IsDotsRuntimeProfilerEnabled = true; - break; - case "ENABLE_PROFILER": - IsProfilerEnabled = true; - break; - case "UNITY_DOTS_DEBUG": - IsDotsDebugMode = true; - break; - } + var node = kvp.Key; + var candidate = kvp.Value; + + var containingMember = node.AncestorOfKind(); + + if (candidateNodesGroupedByContainingMember.TryGetValue(containingMember, out var candidateNodes)) + candidateNodes.Add(node, candidate); + else + candidateNodesGroupedByContainingMember.Add(containingMember, new() + { + { node, candidate } + }); } + + return candidateNodesGroupedByContainingMember; } + } - /// - /// Please avoid using this method if possible, as it does not support node replacements at arbitrary levels of nesting. - /// Instead, implement your own `SystemRewriter` and then add it to the `Rewriters` list. - /// Ideally all existing modules will implement their own rewriters to handle replacements, so that we can remove this method once and for all, - /// which will also greatly simplify the implementation of `PartialSystemTypeGenerator`. - /// - public void ReplaceNodeNonNested(SyntaxNode original, SyntaxNode replacement) - => NonNestedReplacementsInMethods[original] = replacement; - - public bool ContainsChangesToSystem() => - NonNestedReplacementsInMethods.Any() - || HandlesDescription.QueryFieldsToFieldNames.Any() - || HandlesDescription.NonQueryFields.Any() - || FullEcbSystemTypeNamesToGeneratedFieldNames.Any() - || NewMiscellaneousMembers.Any() - || Rewriters.Any(); - - public string GetOrCreateEntityCommandBufferSystemField(ITypeSymbol ecbSystemTypeSymbol) + public bool TryGetSystemStateParameterName(ISystemCandidate candidate, out ExpressionSyntax systemStateExpression) + { + switch (SystemType) { - string fullEcbSystemTypeName = ecbSystemTypeSymbol.ToFullName(); + case SystemType.ISystem: + { + var methodDeclarationSyntax = candidate.Node.AncestorOfKindOrDefault(); + if (methodDeclarationSyntax == null) { + SystemGeneratorErrors.SGSG0001(this, candidate); + systemStateExpression = null; + return false; + } - if (FullEcbSystemTypeNamesToGeneratedFieldNames.TryGetValue(fullEcbSystemTypeName, out var generatedFieldName)) - return generatedFieldName; + var containingMethodSymbol = (IMethodSymbol)ModelExtensions.GetDeclaredSymbol(SemanticModel, methodDeclarationSyntax); - generatedFieldName = $"__{ecbSystemTypeSymbol.ToValidIdentifier()}"; - FullEcbSystemTypeNamesToGeneratedFieldNames[fullEcbSystemTypeName] = generatedFieldName; + var systemStateParameterName = containingMethodSymbol?.Parameters.FirstOrDefault(p => p.Type.Is("Unity.Entities.SystemState"))?.Name; + if (systemStateParameterName != null) + { + systemStateExpression = SyntaxFactory.IdentifierName(systemStateParameterName); + return true; + } - return generatedFieldName; + SystemGeneratorErrors.SGSG0002(this, candidate); + systemStateExpression = null; + return false; + } + case SystemType.Unknown: + systemStateExpression = SyntaxFactory.IdentifierName("state"); + return true; } - public Dictionary GetAnnotationsToOriginalSyntaxNodes(TypeDeclarationSyntax typeDeclarationSyntax) - { - var result = new Dictionary(); - foreach (var memberDeclarationSyntax in SystemTypeSyntax.Members) - result[typeDeclarationSyntax.GetCurrentNode(memberDeclarationSyntax).GetAnnotations(SourceGenHelpers.TrackedNodeAnnotationUsedByRoslyn).First()] = memberDeclarationSyntax; - return result; - } + // this.CheckedStateRef + systemStateExpression = SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.ThisExpression(), SyntaxFactory.IdentifierName("CheckedStateRef")); + return true; } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemGeneratorErrors.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemGeneratorErrors.cs index 72d10a4..8374432 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemGeneratorErrors.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemGeneratorErrors.cs @@ -1,64 +1,62 @@ using Microsoft.CodeAnalysis; using Unity.Entities.SourceGen.Common; -using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.SystemGenerator +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public static class SystemGeneratorErrors { - public static class SystemGeneratorErrors - { - const string k_ErrorTitle = "System Error"; - const string k_SystemStateAccess = "System usage with Missing SystemState"; + const string k_ErrorTitle = "System Error"; + const string k_SystemStateAccess = "System usage with Missing SystemState"; - public static void DC0051(SystemDescription context, Location location, string argumentTypeName, string invokedMethodName) - { - context.LogError(nameof(DC0051), k_ErrorTitle, - $"Type {argumentTypeName} cannot be used with {invokedMethodName} as generic types and parameters are not allowed", location); - } + public static void DC0051(SystemDescription context, Location location, string argumentTypeName, string invokedMethodName) + { + context.LogError(nameof(DC0051), k_ErrorTitle, + $"Type {argumentTypeName} cannot be used with {invokedMethodName} as generic types and parameters are not allowed", location); + } - public static void DC0060(GeneratorExecutionContext context, Location location, string assemblyName) - { - context.LogError(nameof(DC0060), k_ErrorTitle, - $"Assembly {assemblyName} contains Entities.ForEach or Entities.OnUpdate invocations that use burst but does not have a reference to Unity.Burst. Please add an assembly reference to `Unity.Burst` in the asmdef for {assemblyName}.", location); - } + public static void DC0060(GeneratorExecutionContext context, Location location, string assemblyName) + { + context.LogError(nameof(DC0060), k_ErrorTitle, + $"Assembly {assemblyName} contains Entities.ForEach or Entities.OnUpdate invocations that use burst but does not have a reference to Unity.Burst. Please add an assembly reference to `Unity.Burst` in the asmdef for {assemblyName}.", location); + } - public static void DC0061(GeneratorExecutionContext context, Location location, string assemblyName) - { - context.LogError(nameof(DC0061), k_ErrorTitle, - $"Assembly {assemblyName} relies on Unity.Entities which uses the Unity.Collections AllocatorHandle type but does not have a reference to Unity.Collections. Please add an assembly reference to `Unity.Collections` in the asmdef for {assemblyName}.", location); - } + public static void DC0061(GeneratorExecutionContext context, Location location, string assemblyName) + { + context.LogError(nameof(DC0061), k_ErrorTitle, + $"Assembly {assemblyName} relies on Unity.Entities which uses the Unity.Collections AllocatorHandle type but does not have a reference to Unity.Collections. Please add an assembly reference to `Unity.Collections` in the asmdef for {assemblyName}.", location); + } - public static void DC0062(SystemDescription context, Location location) - { - context.LogError(nameof(DC0062), k_ErrorTitle, - $"Only the following methods are allowed when performing a bulk operation on all entities matching a query: `.WithAny()`, `.WithAll()`, `.WithNone()`, `.WithAbsent`, `.WithDisabled`, `WithSharedComponentFilter()`, and `.WithChangeFilter()`.", location); - } + public static void DC0062(SystemDescription context, Location location) + { + context.LogError(nameof(DC0062), k_ErrorTitle, + $"Only the following methods are allowed when performing a bulk operation on all entities matching a query: `.WithAny()`, `.WithAll()`, `.WithNone()`, `.WithAbsent`, `.WithDisabled`, `WithSharedComponentFilter()`, and `.WithChangeFilter()`.", location); + } - public static void DC0063(SystemDescription context, Location location, string methodName, string componentDataName) - { - context.LogError(nameof(DC0063), k_ErrorTitle, - $"Method {methodName} is giving write access to component data {componentDataName} in an Entities.ForEach. The job system cannot guarantee the safety of that invocation. Either change the scheduling from ScheduleParallel to Schedule or access through a captured ComponentLookup and mark it with WithNativeDisableParallelForRestriction if you are certain that this is safe.", location); - } + public static void DC0063(SystemDescription context, Location location, string methodName, string componentDataName) + { + context.LogError(nameof(DC0063), k_ErrorTitle, + $"Method {methodName} is giving write access to component data {componentDataName} in an Entities.ForEach. The job system cannot guarantee the safety of that invocation. Either change the scheduling from ScheduleParallel to Schedule or access through a captured ComponentLookup and mark it with WithNativeDisableParallelForRestriction if you are certain that this is safe.", location); + } - public static void DC0064(SystemDescription context, Location location) - { - context.LogError(nameof(DC0064), k_ErrorTitle, $"WithEntityQueryOptions must be used with a EntityQueryOption value as the argument.", location); - } + public static void DC0064(SystemDescription context, Location location) + { + context.LogError(nameof(DC0064), k_ErrorTitle, $"WithEntityQueryOptions must be used with a EntityQueryOption value as the argument.", location); + } - public static void DC0065(GeneratorExecutionContext context, Location location, string className) - { - context.LogError(nameof(DC0065), k_ErrorTitle, $"Only value type 'ISystem' types are allowed. Make sure {className} is defined as a 'struct' or use 'SystemBase' if you want to use a class.", location); - } + public static void DC0065(GeneratorExecutionContext context, Location location, string className) + { + context.LogError(nameof(DC0065), k_ErrorTitle, $"Only value type 'ISystem' types are allowed. Make sure {className} is defined as a 'struct' or use 'SystemBase' if you want to use a class.", location); + } - public static void SGSG0001(ISourceGeneratorDiagnosable systemDescription, T candidate) where T : ISystemCandidate { - systemDescription.LogError(nameof(SGSG0001), k_SystemStateAccess, - $"SystemState cannot passed in as an argument, as properties can't have parameters, as such {candidate.CandidateTypeName} access is not working on properties. Instead move it to a method passing in `ref SystemState`", - candidate.Node.GetLocation()); - } + public static void SGSG0001(ISourceGeneratorDiagnosable systemDescription, T candidate) where T : ISystemCandidate { + systemDescription.LogError(nameof(SGSG0001), k_SystemStateAccess, + $"SystemState cannot passed in as an argument, as properties can't have parameters, as such {candidate.CandidateTypeName} access is not working on properties. Instead move it to a method passing in `ref SystemState`", + candidate.Node.GetLocation()); + } - public static void SGSG0002(ISourceGeneratorDiagnosable systemDescription, T candidate) where T : ISystemCandidate { - systemDescription.LogError(nameof(SGSG0002), k_SystemStateAccess, - $"No reference to SystemState was found for function with {candidate.CandidateTypeName} access, add `ref SystemState ...` as method parameter. This will be used for updating handles and completing dependencies.", - candidate.Node.GetLocation()); - } + public static void SGSG0002(ISourceGeneratorDiagnosable systemDescription, T candidate) where T : ISystemCandidate { + systemDescription.LogError(nameof(SGSG0002), k_SystemStateAccess, + $"No reference to SystemState was found for function with {candidate.CandidateTypeName} access, add `ref SystemState ...` as method parameter. This will be used for updating handles and completing dependencies.", + candidate.Node.GetLocation()); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemRewriter.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemRewriter.cs deleted file mode 100644 index d3adb94..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemRewriter.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using static Unity.Entities.SourceGen.Common.SourceGenHelpers; - -namespace Unity.Entities.SourceGen.SystemGenerator.Common -{ - public abstract class SystemRewriter : CSharpSyntaxRewriter - { - /// - /// Nodes that require tracking. NOTE: These are NOT the nodes you replace, but the ones on which you need to call `trackedSystem.CurrentNode(...)`. - /// - public abstract IEnumerable NodesToTrack { get; } - - /// - /// `systemRootNode` is the root node of an `ISystem`/`SystemBase` type where replacements/additions of syntax nodes are required. - /// - /// The rewritten system. - public abstract SyntaxNode VisitTrackedSystem(SyntaxNode systemRootNode, string originalFilePath); - /// - /// Original FilePath a.k.a. non-generated version (used for line directives) - /// - protected string m_OriginalFilePath; - - Dictionary m_RewrittenMemberAnnotationToSyntaxNode = new Dictionary(); - public IReadOnlyDictionary RewrittenMemberAnnotationToSyntaxNode => m_RewrittenMemberAnnotationToSyntaxNode; - - /// - /// Updates `RewrittenMemberHashCodeToSyntaxNode` dictionary with info about the newest version of input MemberDeclarationSyntax - /// - protected void RecordChangedMember(MemberDeclarationSyntax changedMember) - { - var syntaxAnnotation = changedMember.GetAnnotations(TrackedNodeAnnotationUsedByRoslyn).First(); - m_RewrittenMemberAnnotationToSyntaxNode[syntaxAnnotation] = changedMember; - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemSyntaxWalker.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemSyntaxWalker.cs new file mode 100644 index 0000000..f6ad806 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemSyntaxWalker.cs @@ -0,0 +1,249 @@ +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.Common +{ + // For each system that contains syntax nodes marked as candidates for potential patching, we create a `SystemSyntaxWalker` instance + // to visit its methods and/or properties that contain these candidates. As the walker traverses through the method/property, it appends + // tokens/trivia as needed to the `_currentMethodAndPropertyWriter`. Whenever it encounters a node that has been marked for potential patching + // by either the `SystemAPIContext` module or the `IFE` module, it relinquishes write control to either the `SystemApiWalker` or the + // `IFESyntaxWalker` respectively. The `SystemApiWalker`/`IFESyntaxWalker` then patches the node if necessary, before ceding write control back + // the `SystemSyntaxWalker`. The `SystemSyntaxWalker` then continues traversing the method/property until it reaches the end. + public class SystemSyntaxWalker : CSharpSyntaxWalker + { + private readonly SystemDescription _systemDescription; + private readonly IndentedTextWriter _currentMethodAndPropertyWriter; + private readonly StringWriter _currentInnerWriter; + + private string _newMemberName; + private SyntaxToken _memberIdentifierToken; + private bool _currentMemberRequiresSourceGen; + private readonly HashSet _statementsRequiringLineDirectives; + + public SystemSyntaxWalker(SystemDescription systemDescription) + : base(SyntaxWalkerDepth.Trivia) + { + _currentInnerWriter = new StringWriter(); + _currentMethodAndPropertyWriter = new IndentedTextWriter(_currentInnerWriter); + _systemDescription = systemDescription; + _statementsRequiringLineDirectives = _systemDescription.GetStatementsRequiringLineDirectives(); + } + + public (bool MethodRequiresSourceGen, string SourceGeneratedMethod) + VisitMethodDeclarationInSystem(MethodDeclarationSyntax methodDeclarationSyntax) + { + var originalMethodSymbol = _systemDescription.SemanticModel.GetDeclaredSymbol(methodDeclarationSyntax); + var targetMethodNameAndSignature = originalMethodSymbol.GetMethodAndParamsAsString(_systemDescription); + var stableHashCode = SourceGenHelpers.GetStableHashCode($"_{targetMethodNameAndSignature}") & 0x7fffffff; + + _newMemberName = $@"__{methodDeclarationSyntax.Identifier.ValueText}_{stableHashCode:X}"; + + // The identifier token for the current method name + _memberIdentifierToken = methodDeclarationSyntax.Identifier; + + // _currentMemberRequiresSourceGen will remain `false` if it turns out that none of the candidate nodes flagged for potential patching are valid candidates + _currentMemberRequiresSourceGen = false; + + // StringWriter.Flush() unfortunately doesn't clear the buffer correctly: https://stackoverflow.com/a/13706647 + // Since IndentedTextWriter.Flush() calls stringWriter.Flush(), we cannot use it either. + _currentInnerWriter.GetStringBuilder().Clear(); + + // Append the `DOTSCompilerPatchedMethod` attribute to the method + _currentMethodAndPropertyWriter.WriteLine($"[global::Unity.Entities.DOTSCompilerPatchedMethod(\"{targetMethodNameAndSignature}\")]"); + + // Begin depth-first traversal of the method + VisitMethodDeclaration(methodDeclarationSyntax); + + return (_currentMemberRequiresSourceGen, _currentMethodAndPropertyWriter.InnerWriter.ToString()); + } + + public (bool PropertyRequiresSourceGen, string SourceGeneratedProperty) + VisitPropertyDeclarationInSystem(PropertyDeclarationSyntax propertyDeclarationSyntax) + { + var originalPropertySymbol = _systemDescription.SemanticModel.GetDeclaredSymbol(propertyDeclarationSyntax); + var targetPropertyNameAndSignature = originalPropertySymbol.OriginalDefinition.ToString(); + var stableHashCode = SourceGenHelpers.GetStableHashCode($"_{targetPropertyNameAndSignature}") & 0x7fffffff; + + _newMemberName = $@"__{propertyDeclarationSyntax.Identifier.ValueText}_{stableHashCode:X}"; + + // The identifier token for the current property name + _memberIdentifierToken = propertyDeclarationSyntax.Identifier; + + // _currentMemberRequiresSourceGen will remain `false` if it turns out that none of the candidate nodes flagged for potential patching are valid candidates + _currentMemberRequiresSourceGen = false; + + // StringWriter.Flush() unfortunately doesn't clear the buffer correctly: https://stackoverflow.com/a/13706647 + // Since IndentedTextWriter.Flush() calls stringWriter.Flush(), we cannot use it either. + _currentInnerWriter.GetStringBuilder().Clear(); + + // Append the `DOTSCompilerPatchedProperty` attribute to the property + _currentMethodAndPropertyWriter.WriteLine($"[global::Unity.Entities.DOTSCompilerPatchedProperty(\"{targetPropertyNameAndSignature}\")]"); + + // Begin depth-first traversal of the property + VisitPropertyDeclaration(propertyDeclarationSyntax); + + return (_currentMemberRequiresSourceGen, _currentMethodAndPropertyWriter.InnerWriter.ToString()); + } + + public override void Visit(SyntaxNode node) + { + if (_statementsRequiringLineDirectives.Contains(node)) + { + foreach (var leadingTrivia in node.GetLeadingTrivia()) + if (leadingTrivia.Kind() == SyntaxKind.WhitespaceTrivia) + // Ensure proper indentation + VisitTrivia(leadingTrivia); + + // Append line directive + _currentMethodAndPropertyWriter.WriteLine($"{GetLineDirective(_systemDescription.OriginalFilePath)}"); + } + base.Visit(node); + + string GetLineDirective(string originalFilePath) + => string.IsNullOrEmpty(originalFilePath) ? "" : $"#line {node.GetLineNumber() + 1} \"{originalFilePath}\""; + } + + public override void VisitExplicitInterfaceSpecifier(ExplicitInterfaceSpecifierSyntax node) + { + } + + public override void VisitAttributeList(AttributeListSyntax node) + { + } + + public override void VisitToken(SyntaxToken token) + { + switch (token.Kind()) + { + case SyntaxKind.OverrideKeyword: + case SyntaxKind.PublicKeyword: + case SyntaxKind.ProtectedKeyword: + case SyntaxKind.PartialKeyword: + case SyntaxKind.VirtualKeyword: + case SyntaxKind.AbstractKeyword: + { + // Do not append the token, only its leading trivia (e.g. preserving whitespace trivia is necessary + // for ensuring neat indentation) + VisitLeadingTrivia(token); + break; + } + default: + { + VisitLeadingTrivia(token); + + // If the current token is the identifier token of the method/property name, append the new method/property name instead + _currentMethodAndPropertyWriter.Write(token == _memberIdentifierToken ? _newMemberName : token.ToString()); + VisitTrailingTrivia(token); + break; + } + } + } + public override void VisitTrivia(SyntaxTrivia trivia) + { + var triviaKind = trivia.Kind(); + + if (triviaKind == SyntaxKind.EndOfLineTrivia) + _currentMethodAndPropertyWriter.WriteLine(); + + else if (triviaKind != SyntaxKind.DisabledTextTrivia && + triviaKind != SyntaxKind.PreprocessingMessageTrivia && + triviaKind != SyntaxKind.IfDirectiveTrivia && + triviaKind != SyntaxKind.ElifDirectiveTrivia && + triviaKind != SyntaxKind.ElseDirectiveTrivia && + triviaKind != SyntaxKind.EndIfDirectiveTrivia && + triviaKind != SyntaxKind.RegionDirectiveTrivia && + triviaKind != SyntaxKind.EndRegionDirectiveTrivia && + triviaKind != SyntaxKind.DefineDirectiveTrivia && + triviaKind != SyntaxKind.UndefDirectiveTrivia && + triviaKind != SyntaxKind.ErrorDirectiveTrivia && + triviaKind != SyntaxKind.WarningDirectiveTrivia && + triviaKind != SyntaxKind.PragmaWarningDirectiveTrivia && + triviaKind != SyntaxKind.PragmaChecksumDirectiveTrivia && + triviaKind != SyntaxKind.ReferenceDirectiveTrivia && + triviaKind != SyntaxKind.BadDirectiveTrivia && + triviaKind != SyntaxKind.SingleLineCommentTrivia && + triviaKind != SyntaxKind.MultiLineCommentTrivia) + { + if (!trivia.HasStructure) + _currentMethodAndPropertyWriter.Write(trivia.ToString()); + } + } + public override void VisitInvocationExpression(InvocationExpressionSyntax node) + { + // If the current node is flagged for potential patching + if (_systemDescription.CandidateNodes.TryGetValue(node, out CandidateSyntax candidateSyntax)) + { + // Cede write control to the appropriate syntax walker by calling `walker.TryWriteSyntax()`. + // If the method returns true, then the controlling walker has written the syntax for the current node. + // If false, then it is the responsibility of the current walker to write the syntax for the current node. + var success = _systemDescription.SyntaxWalkers[candidateSyntax.GetOwningModule()].TryWriteSyntax(_currentMethodAndPropertyWriter, candidateSyntax); + _currentMemberRequiresSourceGen |= success; + + if (!success) + base.VisitInvocationExpression(node); + } + else + base.VisitInvocationExpression(node); + } + + public override void VisitGenericName(GenericNameSyntax node) + { + if (_systemDescription.CandidateNodes.TryGetValue(node, out CandidateSyntax candidateSyntax) && candidateSyntax.Type == CandidateType.Ife) + { + // Cede write control to the `IfeSyntaxWalker`. If it returns `true`, then it has written the syntax for the current node. + var success = _systemDescription.SyntaxWalkers[Module.Ife].TryWriteSyntax(_currentMethodAndPropertyWriter, candidateSyntax); + _currentMemberRequiresSourceGen |= success; + + // If the `IfeSyntaxWalker` did not write the syntax for the current node, + // then it is the responsibility of the current walker to write it. + if (!success) + base.VisitGenericName(node); + } + else + base.VisitGenericName(node); + } + + public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node) + { + // If the current node is flagged for potential patching + if (_systemDescription.CandidateNodes.TryGetValue(node, out CandidateSyntax candidateSyntax) && candidateSyntax.Type <= CandidateType.MaxSystemAPI) + { + // Cede write control to the `SystemApiWalker`. If it returns `true`, then it has written the syntax for the current node. + var success = _systemDescription.SyntaxWalkers[Module.SystemApiContext].TryWriteSyntax(_currentMethodAndPropertyWriter, candidateSyntax); + _currentMemberRequiresSourceGen |= success; + + // If the `SystemApiWalker` did not write the syntax for the current node, + // then it is the responsibility of the current walker to write it. + if (!success) + base.VisitMemberAccessExpression(node); + } + else + base.VisitMemberAccessExpression(node); + } + + public override void VisitIdentifierName(IdentifierNameSyntax node) + { + // If the current node is flagged for potential patching + if (_systemDescription.CandidateNodes.TryGetValue(node, out CandidateSyntax candidateSyntax) && candidateSyntax.Type <= CandidateType.MaxSystemAPI) + { + // Cede write control to the `SystemApiWalker`. If it returns `true`, then it has written the syntax for the current node. + var success = _systemDescription.SyntaxWalkers[Module.SystemApiContext].TryWriteSyntax(_currentMethodAndPropertyWriter, candidateSyntax); + _currentMemberRequiresSourceGen |= success; + + // If the `SystemApiWalker` did not write the syntax for the current node, + // then it is the responsibility of the current walker to write it. + if (!success) + base.VisitIdentifierName(node); + } + else + base.VisitIdentifierName(node); + } + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemTypeHelpers.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemTypeHelpers.cs new file mode 100644 index 0000000..a85eccd --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/SystemTypeHelpers.cs @@ -0,0 +1,16 @@ +using Microsoft.CodeAnalysis; +using Unity.Entities.SourceGen.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.Common; + +public static class SystemTypeHelpers +{ + public static (bool IsSystemType, SystemType SystemType) TryGetSystemType(this ITypeSymbol namedSystemTypeSymbol) + { + if (namedSystemTypeSymbol.Is("Unity.Entities.SystemBase")) + return (true, SystemType.SystemBase); + if (namedSystemTypeSymbol.InheritsFromInterface("Unity.Entities.ISystem")) + return (true, SystemType.ISystem); + return (false, default); + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Unity.Entities.SourceGen.SystemGenerator.Common.csproj b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Unity.Entities.SourceGen.SystemGenerator.Common.csproj index 7809ea2..8b0560b 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Unity.Entities.SourceGen.SystemGenerator.Common.csproj +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.Common/Unity.Entities.SourceGen.SystemGenerator.Common.csproj @@ -3,7 +3,9 @@ true netstandard2.0 - 8.0 + latestmajor + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/BulkOperationRewriter.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/BulkOperationRewriter.cs deleted file mode 100644 index 04b5cd6..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/BulkOperationRewriter.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.SystemGenerator.Common; - -namespace Unity.Entities.SourceGen -{ - public class BulkOperationRewriter : SystemRewriter - { - private readonly Dictionary _rewrittenNodeToReplacementNode; - private readonly IDictionary _originalNodeToReplacementNode; - private bool _replacedNode; - - public override IEnumerable NodesToTrack => _originalNodeToReplacementNode.Keys; - - public BulkOperationRewriter(IDictionary originalNodeToReplacementNode) - { - _rewrittenNodeToReplacementNode = new Dictionary(); - _originalNodeToReplacementNode = originalNodeToReplacementNode; - } - - public override SyntaxNode VisitTrackedSystem(SyntaxNode systemRootNode, string originalFilePath) - { - var originalNodes = _originalNodeToReplacementNode.Select(kvp => kvp.Key).ToArray(); - var replacementNodes = _originalNodeToReplacementNode.Select(kvp => kvp.Value).ToArray(); - var nodesRewrittenDuringTracking = (systemRootNode.GetCurrentNodes(originalNodes) ?? originalNodes).ToArray(); - - for (int i = 0; i < nodesRewrittenDuringTracking.Length; i++) - _rewrittenNodeToReplacementNode.Add(nodesRewrittenDuringTracking[i], replacementNodes[i]); - - return Visit(systemRootNode); - } - - public override SyntaxNode Visit(SyntaxNode syntaxNode) - { - if (syntaxNode == null) - return null; - - var replacedNodeAndChildren = base.Visit(syntaxNode); - - if (syntaxNode is InvocationExpressionSyntax invocationExpressionSyntax - && _rewrittenNodeToReplacementNode.TryGetValue(invocationExpressionSyntax, out var replacementNode)) - { - replacedNodeAndChildren = replacementNode; - _replacedNode = true; - } - - // If we have performed any replacements, we need to update the `RewrittenMemberHashCodeToSyntaxNode` dictionary accordingly - if (replacedNodeAndChildren is MemberDeclarationSyntax memberDeclarationSyntax && _replacedNode) - { - RecordChangedMember(memberDeclarationSyntax); - _replacedNode = false; - } - - return replacedNodeAndChildren; - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/EntityQueryBulkOperationSyntaxWalker.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/EntityQueryBulkOperationSyntaxWalker.cs new file mode 100644 index 0000000..c54e3ba --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/EntityQueryBulkOperationSyntaxWalker.cs @@ -0,0 +1,41 @@ +using System.CodeDom.Compiler; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations +{ + public class EntityQueryBulkOperationSyntaxWalker : CSharpSyntaxWalker, IModuleSyntaxWalker + { + private IndentedTextWriter _writer; + private readonly IDictionary _originalToReplacementNodes; + private bool _hasWrittenSyntax; + + public EntityQueryBulkOperationSyntaxWalker(IDictionary originalToReplacementNodes) : base(SyntaxWalkerDepth.Trivia) => + _originalToReplacementNodes = originalToReplacementNodes; + + public bool TryWriteSyntax(IndentedTextWriter writer, CandidateSyntax candidateSyntax) + { + _writer = writer; + _hasWrittenSyntax = false; + + // Begin depth-first traversal of the candidate node + Visit(candidateSyntax.Node); + + return _hasWrittenSyntax; + } + + public override void VisitInvocationExpression(InvocationExpressionSyntax node) + { + if (_originalToReplacementNodes.TryGetValue(node, out var replacement)) + { + _writer.Write(replacement.ToString()); + _hasWrittenSyntax = true; + } + else + _hasWrittenSyntax = false; + } + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/EntityQueryModule.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/EntityQueryModule.cs index 64d0eea..12c2843 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/EntityQueryModule.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/EntityQueryModule.cs @@ -13,7 +13,7 @@ public class EntityQueryModule : ISystemModule static bool TryGetAllTypeArgumentSymbolsOfMethod( SystemDescription systemDescription, SyntaxNode node, - Dictionary> allMethodInvocations, + IReadOnlyDictionary> allMethodInvocations, string methodName, QueryType resultsShouldHaveThisQueryType, out List result) @@ -21,7 +21,6 @@ static bool TryGetAllTypeArgumentSymbolsOfMethod( result = new List(); var isValid = true; - var semanticModel = systemDescription.SemanticModel; var location = node.GetLocation(); if (!allMethodInvocations.ContainsKey(methodName)) @@ -29,7 +28,7 @@ static bool TryGetAllTypeArgumentSymbolsOfMethod( var symbols = allMethodInvocations[methodName] - .Select(methodInvocation => (IMethodSymbol)semanticModel.GetSymbolInfo(methodInvocation).Symbol) + .Select(methodInvocation => (IMethodSymbol)systemDescription.SemanticModel.GetSymbolInfo(methodInvocation).Symbol) .Where(symbol => symbol != null); foreach (var symbol in symbols) @@ -60,29 +59,13 @@ static bool TryGetAllTypeArgumentSymbolsOfMethod( get { foreach (var kvp in EntityQueryCandidatesGroupedBySystemType) - foreach (var candidate in kvp.Value) - yield return (candidate.EntitiesSyntaxNode, candidate.ContainingSystemType); + foreach (var node in kvp.Value) + yield return (node, kvp.Key); } } public bool RequiresReferenceToBurst => false; - - public struct QueryCandidate - { - public SyntaxNode EntitiesSyntaxNode { get; private set; } - public TypeDeclarationSyntax ContainingSystemType { get; private set; } - - public static QueryCandidate From(SyntaxNode entitiesSyntaxNode) - { - return new QueryCandidate - { - EntitiesSyntaxNode = entitiesSyntaxNode, - ContainingSystemType = entitiesSyntaxNode.Ancestors().OfType().First(), - }; - } - } - - Dictionary> EntityQueryCandidatesGroupedBySystemType { get; } = new Dictionary>(); + Dictionary> EntityQueryCandidatesGroupedBySystemType { get; } = new Dictionary>(); static string[] BulkOperationMethodNames { get; } = { @@ -99,14 +82,13 @@ public static QueryCandidate From(SyntaxNode entitiesSyntaxNode) "ToQuery", }; - public void OnReceiveSyntaxNode(SyntaxNode entitiesSyntaxNode) + public void OnReceiveSyntaxNode(SyntaxNode entitiesSyntaxNode, Dictionary candidateOwnership) { if (entitiesSyntaxNode is IdentifierNameSyntax identifierNameSyntax && identifierNameSyntax.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression) && identifierNameSyntax.Identifier.Text == "Entities") { - var newQueryCandidate = QueryCandidate.From(entitiesSyntaxNode); - EntityQueryCandidatesGroupedBySystemType.Add(newQueryCandidate.ContainingSystemType, newQueryCandidate); + EntityQueryCandidatesGroupedBySystemType.Add(entitiesSyntaxNode.Ancestors().OfType().First(), entitiesSyntaxNode); } } @@ -124,7 +106,7 @@ public bool RegisterChangesInSystem(SystemDescription systemDescription) var bulkOperationQueryMethodInvocations = new Dictionary>(); - foreach (var invocationExpressionSyntax in candidate.EntitiesSyntaxNode.Ancestors().OfType()) + foreach (var invocationExpressionSyntax in candidate.Ancestors().OfType()) { bool isEntitiesForEachInvocation = false; @@ -181,6 +163,9 @@ public bool RegisterChangesInSystem(SystemDescription systemDescription) case "WithAbsent": bulkOperationQueryMethodInvocations.Add("WithAbsent", invocationExpressionSyntax); break; + case "WithPresent": + bulkOperationQueryMethodInvocations.Add("WithPresent", invocationExpressionSyntax); + break; case "WithChangeFilter": bulkOperationQueryMethodInvocations.Add("WithChangeFilter", invocationExpressionSyntax); break; @@ -204,17 +189,18 @@ public bool RegisterChangesInSystem(SystemDescription systemDescription) if (foundMethodInvocationsDisallowedByBulkOperations) { - SystemGeneratorErrors.DC0062(systemDescription, candidate.EntitiesSyntaxNode.GetLocation()); + SystemGeneratorErrors.DC0062(systemDescription, candidate.GetLocation()); break; } - success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate.EntitiesSyntaxNode, bulkOperationQueryMethodInvocations, "WithAll", QueryType.All, out var withAllTypes); - success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate.EntitiesSyntaxNode, bulkOperationQueryMethodInvocations, "WithAny", QueryType.Any, out var withAnyTypes); - success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate.EntitiesSyntaxNode, bulkOperationQueryMethodInvocations, "WithNone", QueryType.None, out var withNoneTypes); - success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate.EntitiesSyntaxNode, bulkOperationQueryMethodInvocations, "WithDisabled", QueryType.Disabled, out var withDisabledTypes); - success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate.EntitiesSyntaxNode, bulkOperationQueryMethodInvocations, "WithAbsent", QueryType.Absent, out var withAbsentTypes); - success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate.EntitiesSyntaxNode, bulkOperationQueryMethodInvocations, "WithChangeFilter", QueryType.ChangeFilter, out var withChangeFilterTypes); - success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate.EntitiesSyntaxNode, bulkOperationQueryMethodInvocations, "WithSharedComponentFilter", QueryType.All, out var withSharedComponentFilterTypes); + success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate, bulkOperationQueryMethodInvocations, "WithAll", QueryType.All, out var withAllTypes); + success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate, bulkOperationQueryMethodInvocations, "WithAny", QueryType.Any, out var withAnyTypes); + success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate, bulkOperationQueryMethodInvocations, "WithNone", QueryType.None, out var withNoneTypes); + success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate, bulkOperationQueryMethodInvocations, "WithDisabled", QueryType.Disabled, out var withDisabledTypes); + success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate, bulkOperationQueryMethodInvocations, "WithAbsent", QueryType.Absent, out var withAbsentTypes); + success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate, bulkOperationQueryMethodInvocations, "WithPresent", QueryType.Absent, out var withPresentTypes); + success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate, bulkOperationQueryMethodInvocations, "WithChangeFilter", QueryType.ChangeFilter, out var withChangeFilterTypes); + success &= TryGetAllTypeArgumentSymbolsOfMethod(systemDescription, candidate, bulkOperationQueryMethodInvocations, "WithSharedComponentFilter", QueryType.All, out var withSharedComponentFilterTypes); var queryDescription = new SingleArchetypeQueryFieldDescription( @@ -223,10 +209,15 @@ public bool RegisterChangesInSystem(SystemDescription systemDescription) withAnyTypes, withNoneTypes, withDisabledTypes, + withPresentTypes, withAbsentTypes), changeFilterTypes: withChangeFilterTypes); - var generatedQueryFieldName = systemDescription.HandlesDescription.GetOrCreateQueryField(queryDescription); + var generatedQueryFieldName = systemDescription.QueriesAndHandles.GetOrCreateQueryField(queryDescription); + + systemDescription.CandidateNodes.Add( + bulkOperationInvocationNodeToReplace, + new CandidateSyntax(CandidateType.EntityQueryBulkOps, CandidateFlags.None, bulkOperationInvocationNodeToReplace)); if (bulkOperationInvocationText != "ToQuery") { @@ -244,16 +235,11 @@ public bool RegisterChangesInSystem(SystemDescription systemDescription) originalToReplacementNodes.Add(bulkOperationInvocationNodeToReplace, replacementSyntaxNode); } else - { originalToReplacementNodes.Add(bulkOperationInvocationNodeToReplace, SyntaxFactory.IdentifierName(generatedQueryFieldName)); - } } if (originalToReplacementNodes.Count > 0) - { - var bulkOperationRewriter = new BulkOperationRewriter(originalToReplacementNodes); - systemDescription.Rewriters.Add(bulkOperationRewriter); - } + systemDescription.SyntaxWalkers.Add(Module.EntityQueryBulkOps, new EntityQueryBulkOperationSyntaxWalker(originalToReplacementNodes)); return success; } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.csproj b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.csproj index 21fdc69..1000ba7 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.csproj +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.EntityQueryBulkOperations/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.csproj @@ -4,6 +4,8 @@ true netstandard2.0 8.0 + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ArgumentParserForEntityCommandBufferMethods.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ArgumentParserForEntityCommandBufferMethods.cs index 267bc5c..f120326 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ArgumentParserForEntityCommandBufferMethods.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ArgumentParserForEntityCommandBufferMethods.cs @@ -5,173 +5,172 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +internal static class ArgumentParserForEntityCommandBufferMethods { - internal static class ArgumentParserForEntityCommandBufferMethods + internal class ParserException : Exception { - internal class ParserException : Exception + public ParserException(string errorMessage) : base(errorMessage) { - public ParserException(string errorMessage) : base(errorMessage) - { - } } + } - internal class ArgumentValues - { - public string BooleanValue { get; set; } - public string BufferElementData { get; set; } - public string ComponentData { get; set; } - public string ComponentType { get; set; } - public string ComponentTypeSet { get; set; } - public string Entity { get; set; } - public string EntityArchetype { get; set; } - public string EntitiesNativeArray { get; set; } - public string GenericTypeArgument { get; set; } - public string FixedString64Bytes { get; set; } - public string SharedComponentData { get; set; } - } + internal class ArgumentValues + { + public string BooleanValue { get; set; } + public string BufferElementData { get; set; } + public string ComponentData { get; set; } + public string ComponentType { get; set; } + public string ComponentTypeSet { get; set; } + public string Entity { get; set; } + public string EntityArchetype { get; set; } + public string EntitiesNativeArray { get; set; } + public string GenericTypeArgument { get; set; } + public string FixedString64Bytes { get; set; } + public string SharedComponentData { get; set; } + } - public static ArgumentValues Parse(InvocationExpressionSyntax invocationExpressionSyntax, IMethodSymbol method) - { - var results = new ArgumentValues(); + public static ArgumentValues Parse(InvocationExpressionSyntax invocationExpressionSyntax, IMethodSymbol method) + { + var results = new ArgumentValues(); - var passedArguments = - invocationExpressionSyntax.ChildNodes().OfType().SelectMany(list => list.Arguments).ToArray(); + var passedArguments = + invocationExpressionSyntax.ChildNodes().OfType().SelectMany(list => list.Arguments).ToArray(); - for (int i = 0; i < passedArguments.Length; i++) - { - var argument = passedArguments[i]; - var namedArgument = argument.ChildNodes().OfType().SingleOrDefault(); - - var (type, value) = namedArgument == null - ? ParseUnnamedArgument(argument, i, method.Parameters) - : ParseNamedArgument(argument, argumentName: namedArgument.Name.Identifier.ValueText); - - switch (type) - { - case ArgumentType.Bool: - results.BooleanValue = value; - break; - case ArgumentType.BufferElementData: - results.BufferElementData = value; - break; - case ArgumentType.ComponentDataStruct: - results.ComponentData = value; - break; - case ArgumentType.ComponentType: - results.ComponentType = value; - break; - case ArgumentType.ComponentTypeSet: - results.ComponentTypeSet = value; - break; - case ArgumentType.Entity: - results.Entity = value; - break; - case ArgumentType.EntitiesNativeArray: - results.EntitiesNativeArray = value; - break; - case ArgumentType.EntityArchetype: - results.EntityArchetype = value; - break; - case ArgumentType.FixedString64Bytes: - results.FixedString64Bytes = value; - break; - case ArgumentType.SharedComponentData: - results.SharedComponentData = value; - break; - } - } + for (int i = 0; i < passedArguments.Length; i++) + { + var argument = passedArguments[i]; + var namedArgument = argument.ChildNodes().OfType().SingleOrDefault(); - bool mustSpecifyGenericTypeArgument = - method.Name != "CreateEntity" - && method.Name != "DestroyEntity" - && method.Name != "Instantiate" - && String.IsNullOrEmpty(results.ComponentData) - && String.IsNullOrEmpty(results.ComponentType) - && String.IsNullOrEmpty(results.ComponentTypeSet) - && String.IsNullOrEmpty(results.SharedComponentData) - && String.IsNullOrEmpty(results.BufferElementData) - && String.IsNullOrEmpty(results.FixedString64Bytes); - - if (mustSpecifyGenericTypeArgument) + var (type, value) = namedArgument == null + ? ParseUnnamedArgument(argument, i, method.Parameters) + : ParseNamedArgument(argument, argumentName: namedArgument.Name.Identifier.ValueText); + + switch (type) { - var memberAccessExpressionSyntax = invocationExpressionSyntax.Expression as MemberAccessExpressionSyntax; - results.GenericTypeArgument = ((GenericNameSyntax)memberAccessExpressionSyntax?.Name).ToString(); + case ArgumentType.Bool: + results.BooleanValue = value; + break; + case ArgumentType.BufferElementData: + results.BufferElementData = value; + break; + case ArgumentType.ComponentDataStruct: + results.ComponentData = value; + break; + case ArgumentType.ComponentType: + results.ComponentType = value; + break; + case ArgumentType.ComponentTypeSet: + results.ComponentTypeSet = value; + break; + case ArgumentType.Entity: + results.Entity = value; + break; + case ArgumentType.EntitiesNativeArray: + results.EntitiesNativeArray = value; + break; + case ArgumentType.EntityArchetype: + results.EntityArchetype = value; + break; + case ArgumentType.FixedString64Bytes: + results.FixedString64Bytes = value; + break; + case ArgumentType.SharedComponentData: + results.SharedComponentData = value; + break; } - - return results; } - public static (ArgumentType Type, string Value) ParseNamedArgument(ArgumentSyntax argument, string argumentName) + bool mustSpecifyGenericTypeArgument = + method.Name != "CreateEntity" + && method.Name != "DestroyEntity" + && method.Name != "Instantiate" + && String.IsNullOrEmpty(results.ComponentData) + && String.IsNullOrEmpty(results.ComponentType) + && String.IsNullOrEmpty(results.ComponentTypeSet) + && String.IsNullOrEmpty(results.SharedComponentData) + && String.IsNullOrEmpty(results.BufferElementData) + && String.IsNullOrEmpty(results.FixedString64Bytes); + + if (mustSpecifyGenericTypeArgument) { - var argumentValue = argument.Expression.ToString(); - - return argumentName switch - { - "archetype" => (ArgumentType.EntityArchetype, argumentValue), - "e" => (ArgumentType.Entity, argumentValue), - "entities" => (ArgumentType.EntitiesNativeArray, argumentValue), - "component" => (ArgumentType.ComponentDataStruct, argumentValue), - "sharedComponent" => (ArgumentType.SharedComponentData, argumentValue), - "componentType" => (ArgumentType.ComponentType, argumentValue), - "componentTypeSet" => (ArgumentType.ComponentTypeSet, argumentValue), - "name" => (ArgumentType.FixedString64Bytes, argumentValue), - "value" => (ArgumentType.Bool, argumentValue), - _ => throw new ArgumentOutOfRangeException() - }; + var memberAccessExpressionSyntax = invocationExpressionSyntax.Expression as MemberAccessExpressionSyntax; + results.GenericTypeArgument = ((GenericNameSyntax)memberAccessExpressionSyntax?.Name).ToString(); } - public static (ArgumentType Type, string Value) ParseUnnamedArgument(ArgumentSyntax argument, int position, IEnumerable methodParameters) + return results; + } + + static (ArgumentType Type, string Value) ParseNamedArgument(ArgumentSyntax argument, string argumentName) + { + var argumentValue = argument.Expression.ToString(); + + return argumentName switch { - var argumentTypes = methodParameters.Select(GetArgumentType).ToArray(); - return (argumentTypes[position], argument.Expression.ToString()); + "archetype" => (ArgumentType.EntityArchetype, argumentValue), + "e" => (ArgumentType.Entity, argumentValue), + "entities" => (ArgumentType.EntitiesNativeArray, argumentValue), + "component" => (ArgumentType.ComponentDataStruct, argumentValue), + "sharedComponent" => (ArgumentType.SharedComponentData, argumentValue), + "componentType" => (ArgumentType.ComponentType, argumentValue), + "componentTypeSet" => (ArgumentType.ComponentTypeSet, argumentValue), + "name" => (ArgumentType.FixedString64Bytes, argumentValue), + "value" => (ArgumentType.Bool, argumentValue), + _ => throw new ArgumentOutOfRangeException() + }; + } + + static (ArgumentType Type, string Value) ParseUnnamedArgument(ArgumentSyntax argument, int position, IEnumerable methodParameters) + { + var argumentTypes = methodParameters.Select(GetArgumentType).ToArray(); + return (argumentTypes[position], argument.Expression.ToString()); - ArgumentType GetArgumentType(IParameterSymbol parameterSymbol) + ArgumentType GetArgumentType(IParameterSymbol parameterSymbol) + { + switch (parameterSymbol.Type.ToFullName()) { - switch (parameterSymbol.Type.ToFullName()) - { - case "global::System.Boolean": - case "bool": - return ArgumentType.Bool; - case "global::Unity.Entities.ComponentType": - return ArgumentType.ComponentType; - case "global::Unity.Entities.ComponentTypeSet": - return ArgumentType.ComponentTypeSet; - case "global::Unity.Entities.Entity": - return ArgumentType.Entity; - case "global::Unity.Entities.EntityArchetype": - return ArgumentType.EntityArchetype; - case "global::Unity.Collections.NativeArray": - return ArgumentType.EntitiesNativeArray; - case "global::Unity.Collections.FixedString64Bytes": - return ArgumentType.FixedString64Bytes; - } - - if (parameterSymbol.Type.ImplementsInterface("Unity.Entities.IComponentData")) - return ArgumentType.ComponentDataStruct; - - if (parameterSymbol.Type.ImplementsInterface("Unity.Entities.IBufferElementData")) - return ArgumentType.BufferElementData; - - if (parameterSymbol.Type.ImplementsInterface("Unity.Entities.ISharedComponentData")) - return ArgumentType.SharedComponentData; - - throw new ArgumentOutOfRangeException(); + case "global::System.Boolean": + case "bool": + return ArgumentType.Bool; + case "global::Unity.Entities.ComponentType": + return ArgumentType.ComponentType; + case "global::Unity.Entities.ComponentTypeSet": + return ArgumentType.ComponentTypeSet; + case "global::Unity.Entities.Entity": + return ArgumentType.Entity; + case "global::Unity.Entities.EntityArchetype": + return ArgumentType.EntityArchetype; + case "global::Unity.Collections.NativeArray": + return ArgumentType.EntitiesNativeArray; + case "global::Unity.Collections.FixedString64Bytes": + return ArgumentType.FixedString64Bytes; } - } - internal enum ArgumentType - { - Bool, - BufferElementData, - ComponentDataStruct, - ComponentType, - ComponentTypeSet, - EntitiesNativeArray, - Entity, - EntityArchetype, - FixedString64Bytes, - SharedComponentData, + if (parameterSymbol.Type.ImplementsInterface("Unity.Entities.IComponentData")) + return ArgumentType.ComponentDataStruct; + + if (parameterSymbol.Type.ImplementsInterface("Unity.Entities.IBufferElementData")) + return ArgumentType.BufferElementData; + + if (parameterSymbol.Type.ImplementsInterface("Unity.Entities.ISharedComponentData")) + return ArgumentType.SharedComponentData; + + throw new ArgumentOutOfRangeException(); } } + + enum ArgumentType + { + Bool, + BufferElementData, + ComponentDataStruct, + ComponentType, + ComponentTypeSet, + EntitiesNativeArray, + Entity, + EntityArchetype, + FixedString64Bytes, + SharedComponentData, + } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/DataLookupFieldDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/DataLookupFieldDescription.cs index fba3594..4677bea 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/DataLookupFieldDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/DataLookupFieldDescription.cs @@ -4,84 +4,83 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +class DataLookupFieldDescription { - class DataLookupFieldDescription - { - public bool IsReadOnly { get; } - public ITypeSymbol Type { get; } - public LambdaJobsPatchableMethod.AccessorDataType AccessorDataType { get; } - public string FieldName { get; } + public bool IsReadOnly { get; } + public ITypeSymbol Type { get; } + public LambdaJobsPatchableMethod.AccessorDataType AccessorDataType { get; } + public string FieldName { get; } - public DataLookupFieldDescription(bool isReadOnly, ITypeSymbol type, LambdaJobsPatchableMethod.AccessorDataType accessorDataType) + public DataLookupFieldDescription(bool isReadOnly, ITypeSymbol type, LambdaJobsPatchableMethod.AccessorDataType accessorDataType) + { + IsReadOnly = isReadOnly; + Type = type; + AccessorDataType = accessorDataType; + FieldName = AccessorDataType switch { - IsReadOnly = isReadOnly; - Type = type; - AccessorDataType = accessorDataType; - FieldName = AccessorDataType switch - { - LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup => - $"__{Type.ToValidIdentifier()}_ComponentLookup", - LambdaJobsPatchableMethod.AccessorDataType.BufferLookup => - $"__{Type.ToValidIdentifier()}_BufferLookup", - LambdaJobsPatchableMethod.AccessorDataType.AspectLookup => - $"__{Type.ToValidIdentifier()}_AspectLookup", - LambdaJobsPatchableMethod.AccessorDataType.EntityStorageInfoLookup => - "__EntityStorageInfoLookup", - _ => throw new ArgumentOutOfRangeException($"Passed in invalid {nameof(LambdaJobsPatchableMethod.AccessorDataType)} enum") - }; - } + LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup => + $"__{Type.ToValidIdentifier()}_ComponentLookup", + LambdaJobsPatchableMethod.AccessorDataType.BufferLookup => + $"__{Type.ToValidIdentifier()}_BufferLookup", + LambdaJobsPatchableMethod.AccessorDataType.AspectLookup => + $"__{Type.ToValidIdentifier()}_AspectLookup", + LambdaJobsPatchableMethod.AccessorDataType.EntityStorageInfoLookup => + "__EntityStorageInfoLookup", + _ => throw new ArgumentOutOfRangeException($"Passed in invalid {nameof(LambdaJobsPatchableMethod.AccessorDataType)} enum") + }; + } - public string JobStructAssign() + public string JobStructAssign() + { + return AccessorDataType switch { - return AccessorDataType switch - { - LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup => - $"{FieldName} = __TypeHandle.__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_ComponentLookup", - LambdaJobsPatchableMethod.AccessorDataType.BufferLookup => - $"{FieldName} = __TypeHandle.__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_BufferLookup", - LambdaJobsPatchableMethod.AccessorDataType.AspectLookup => - $"{FieldName} = __TypeHandle.__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_AspectLookup", - LambdaJobsPatchableMethod.AccessorDataType.EntityStorageInfoLookup => - $"{FieldName} = __TypeHandle.__EntityStorageInfoLookup", - _ => throw new ArgumentOutOfRangeException($"Passed in invalid {nameof(LambdaJobsPatchableMethod.AccessorDataType)} enum") - }; - } - public string FormatUpdateInvocation() + LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup => + $"{FieldName} = __TypeHandle.__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_ComponentLookup", + LambdaJobsPatchableMethod.AccessorDataType.BufferLookup => + $"{FieldName} = __TypeHandle.__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_BufferLookup", + LambdaJobsPatchableMethod.AccessorDataType.AspectLookup => + $"{FieldName} = __TypeHandle.__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_AspectLookup", + LambdaJobsPatchableMethod.AccessorDataType.EntityStorageInfoLookup => + $"{FieldName} = __TypeHandle.__EntityStorageInfoLookup", + _ => throw new ArgumentOutOfRangeException($"Passed in invalid {nameof(LambdaJobsPatchableMethod.AccessorDataType)} enum") + }; + } + public string FormatUpdateInvocation() + { + var fieldNameWithReadAccess = AccessorDataType switch { - var fieldNameWithReadAccess = AccessorDataType switch - { - LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup => - $"__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_ComponentLookup", - LambdaJobsPatchableMethod.AccessorDataType.BufferLookup => - $"__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_BufferLookup", - LambdaJobsPatchableMethod.AccessorDataType.AspectLookup => - $"__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_AspectLookup", - LambdaJobsPatchableMethod.AccessorDataType.EntityStorageInfoLookup => - "__EntityStorageInfoLookup", - _ => throw new ArgumentOutOfRangeException($"Passed in invalid {nameof(LambdaJobsPatchableMethod.AccessorDataType)} enum") - }; - return $@"__TypeHandle.{fieldNameWithReadAccess}.Update(ref this.CheckedStateRef);"; - } + LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup => + $"__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_ComponentLookup", + LambdaJobsPatchableMethod.AccessorDataType.BufferLookup => + $"__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_BufferLookup", + LambdaJobsPatchableMethod.AccessorDataType.AspectLookup => + $"__{Type.ToValidIdentifier()}_{(IsReadOnly ? "RO" : "RW")}_AspectLookup", + LambdaJobsPatchableMethod.AccessorDataType.EntityStorageInfoLookup => + "__EntityStorageInfoLookup", + _ => throw new ArgumentOutOfRangeException($"Passed in invalid {nameof(LambdaJobsPatchableMethod.AccessorDataType)} enum") + }; + return $@"__TypeHandle.{fieldNameWithReadAccess}.Update(ref this.CheckedStateRef);"; + } - public FieldDeclarationSyntax ToFieldDeclaration() - { - var accessAttribute = IsReadOnly ? "[Unity.Collections.ReadOnly]" : string.Empty; + public FieldDeclarationSyntax ToFieldDeclaration() + { + var accessAttribute = IsReadOnly ? "[Unity.Collections.ReadOnly]" : string.Empty; - var dataLookupType = AccessorDataType switch - { - LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup => "Unity.Entities.ComponentLookup", - LambdaJobsPatchableMethod.AccessorDataType.BufferLookup => "Unity.Entities.BufferLookup", - LambdaJobsPatchableMethod.AccessorDataType.EntityStorageInfoLookup => "Unity.Entities.EntityStorageInfoLookup", - LambdaJobsPatchableMethod.AccessorDataType.AspectLookup => $"{Type.ToFullName()}.Lookup", - _ => throw new ArgumentOutOfRangeException($"Passed in invalid {nameof(LambdaJobsPatchableMethod.AccessorDataType)} enum") - }; + var dataLookupType = AccessorDataType switch + { + LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup => "Unity.Entities.ComponentLookup", + LambdaJobsPatchableMethod.AccessorDataType.BufferLookup => "Unity.Entities.BufferLookup", + LambdaJobsPatchableMethod.AccessorDataType.EntityStorageInfoLookup => "Unity.Entities.EntityStorageInfoLookup", + LambdaJobsPatchableMethod.AccessorDataType.AspectLookup => $"{Type.ToFullName()}.Lookup", + _ => throw new ArgumentOutOfRangeException($"Passed in invalid {nameof(LambdaJobsPatchableMethod.AccessorDataType)} enum") + }; - var typeArgumentSnippet = $"<{Type.ToFullName()}>".EmitIfTrue(AccessorDataType == LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup || AccessorDataType == LambdaJobsPatchableMethod.AccessorDataType.BufferLookup); - var template = $@"{accessAttribute} public {dataLookupType}{typeArgumentSnippet} {FieldName};"; + var typeArgumentSnippet = $"<{Type.ToFullName()}>".EmitIfTrue(AccessorDataType == LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup || AccessorDataType == LambdaJobsPatchableMethod.AccessorDataType.BufferLookup); + var template = $@"{accessAttribute} public {dataLookupType}{typeArgumentSnippet} {FieldName};"; - return (FieldDeclarationSyntax) SyntaxFactory.ParseMemberDeclaration(template); - } + return (FieldDeclarationSyntax) SyntaxFactory.ParseMemberDeclaration(template); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/EntitiesForEachSyntaxWalker.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/EntitiesForEachSyntaxWalker.cs new file mode 100644 index 0000000..9309a15 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/EntitiesForEachSyntaxWalker.cs @@ -0,0 +1,42 @@ +using System.CodeDom.Compiler; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +public class EntitiesForEachSyntaxWalker : CSharpSyntaxWalker, IModuleSyntaxWalker +{ + private readonly Dictionary _originalToReplacementNodes; + + private IndentedTextWriter _writer; + private bool _hasWrittenSyntax; + + public EntitiesForEachSyntaxWalker(Dictionary originalToReplacementNodes) : base(SyntaxWalkerDepth.Trivia) => + _originalToReplacementNodes = originalToReplacementNodes; + + public bool TryWriteSyntax(IndentedTextWriter writer, CandidateSyntax candidateSyntax) + { + _writer = writer; + _hasWrittenSyntax = false; + + // Begin depth-first traversal of the candidate node + Visit(candidateSyntax.Node); + + return _hasWrittenSyntax; + } + + public override void VisitInvocationExpression(InvocationExpressionSyntax node) + { + if (_originalToReplacementNodes.TryGetValue(node, out var replacement)) + { + _writer.WriteLine(); + _writer.Write(replacement); + _hasWrittenSyntax = true; + } + else + _hasWrittenSyntax = false; + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/EntitiesSourceFactory.Common.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/EntitiesSourceFactory.Common.cs deleted file mode 100644 index 9460300..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/EntitiesSourceFactory.Common.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Collections.Generic; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Linq; -using System.Text; -using Unity.Entities.SourceGen.Common; - -namespace Unity.Entities.SourceGen.LambdaJobs -{ - public static partial class EntitiesSourceFactory - { - public static class Common - { - public static string NoAliasAttribute(LambdaJobDescription description) => - description.Burst.IsEnabled ? @"[global::Unity.Burst.NoAlias]" : string.Empty; - - public static string BurstCompileAttribute(LambdaJobDescription description) - { - if (description.Burst.IsEnabled) - { - var parameters = new List(); - if (description.Burst.Settings.BurstFloatMode != null) - parameters.Add($"FloatMode=global::Unity.Burst.FloatMode.{description.Burst.Settings.BurstFloatMode}"); - if (description.Burst.Settings.BurstFloatPrecision != null) - parameters.Add($"FloatPrecision=global::Unity.Burst.FloatPrecision.{description.Burst.Settings.BurstFloatPrecision}"); - if (description.Burst.Settings.SynchronousCompilation != null) - parameters.Add($"CompileSynchronously={description.Burst.Settings.SynchronousCompilation.ToString().ToLower()}"); - - return parameters.Count == 0 - ? "[global::Unity.Burst.BurstCompile]" - : $"[global::Unity.Burst.BurstCompile({string.Join(", ", parameters)})]"; - } - else - return string.Empty; - } - - public static string MonoPInvokeCallbackAttributeAttribute(LambdaJobDescription description) => - description.LambdaJobKind == LambdaJobKind.Entities ? - $@"[global::AOT.MonoPInvokeCallback(typeof(global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate))]" : - $@"[global::AOT.MonoPInvokeCallback(typeof(global::Unity.Entities.Internal.InternalCompilerInterface.JobRunWithoutJobSystemDelegate))]"; - - public static SyntaxNode SchedulingInvocationFor(LambdaJobDescription description) - { - static string ExecuteMethodArgs(LambdaJobDescription description) - { - var argStrings = new HashSet(); - foreach (var variable in description.VariablesCaptured) - { - if (!variable.IsThis) - argStrings.Add(description.Schedule.Mode == ScheduleMode.Run && variable.IsWritable - ? $"ref {variable.OriginalVariableName}" - : variable.OriginalVariableName); - } - - if (description.Schedule.DependencyArgument != null) - argStrings.Add($@"{description.Schedule.DependencyArgument.ToString()}"); - - if (description.WithFilterEntityArray != null) - argStrings.Add($@"{description.WithFilterEntityArray.ToString()}"); - - foreach (var argument in description.AdditionalVariablesCapturedForScheduling) - argStrings.Add(argument.Name); - - return argStrings.SeparateByComma(); - } - - var template = $@"{description.ExecuteInSystemMethodName}({ExecuteMethodArgs(description)}));"; - return SyntaxFactory.ParseStatement(template).DescendantNodes().OfType().FirstOrDefault(); - } - - public static string SharedComponentFilterInvocations(LambdaJobDescription description) - { - if (!description.HasSharedComponentFilter) - return string.Empty; - - return - description - .WithSharedComponentFilterArgumentSyntaxes - .Select(arg => $@"{description.EntityQueryFieldName}.SetSharedComponentFilter({arg});") - .SeparateByNewLine(); - } - - public static string ResetSharedComponentFilter(LambdaJobDescription description) => - !description.HasSharedComponentFilter ? string.Empty : $@"{description.EntityQueryFieldName}.ResetFilter();"; - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/EntitiesSourceFactory.LambdaJobs.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/EntitiesSourceFactory.LambdaJobs.cs deleted file mode 100644 index 3a19f36..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/EntitiesSourceFactory.LambdaJobs.cs +++ /dev/null @@ -1,820 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.Common; -using static Unity.Entities.SourceGen.LambdaJobs.LambdaParamDescription_EntityCommandBuffer; - -namespace Unity.Entities.SourceGen.LambdaJobs -{ - static partial class EntitiesSourceFactory - { - public static class LambdaJobs - { - public static StructDeclarationSyntax JobStructFor(LambdaJobDescription description) - { - var template = $@" - {TypeCreationHelpers.GeneratedLineTriviaToGeneratedSource} - {Common.NoAliasAttribute(description)} - {Common.BurstCompileAttribute(description)} - {(description.NeedsUnsafe ? "unsafe " : string.Empty)}struct {description.JobStructName} - {JobInterface()} - {{ - {(description.IsForDOTSRuntime ? IJobBaseMethods(description) : string.Empty)} - {(description.NeedsEntityInQueryIndex ? ChunkBaseEntityIndicesField() : string.Empty)} - {RunWithoutJobSystemDelegateFields(description).EmitIfTrue(description.NeedsJobFunctionPointers)} - {StructSystemFields()} - {CapturedVariableFields()} - {TypeHandleFields().EmitIfTrue(!description.WithStructuralChanges)} - {AdditionalDataLookupFields()} - {GenerateProfilerMarker(description)} - - {OriginalLambdaBody()} - - {ExecuteMethod()} - {DisposeOnCompletionMethod()} - - {RunWithoutJobSystemMethod(description).EmitIfTrue(description.NeedsJobFunctionPointers)} - }}"; - - static string ChunkBaseEntityIndicesField() => - @"[global::Unity.Collections.ReadOnly] - public global::Unity.Collections.NativeArray __ChunkBaseEntityIndices;"; - - var jobStructDeclaration = (StructDeclarationSyntax) SyntaxFactory.ParseMemberDeclaration(template); - - // Find lambda body in job struct template and replace rewritten lambda body into method - var templateLambdaMethodBody = jobStructDeclaration.DescendantNodes() - .OfType().First( - method => method.Identifier.ValueText == "OriginalLambdaBody").DescendantNodes() - .OfType().First(); - jobStructDeclaration = jobStructDeclaration.ReplaceNode(templateLambdaMethodBody, - description.RewrittenLambdaBody.WithoutPreprocessorTrivia()); - - return jobStructDeclaration; - - static string GetChunkNativeArrays(LambdaJobDescription description) => - description.LambdaParameters.Select(param => param.GetNativeArrayOrAccessor()).SeparateByNewLine(); - - //var rotationTypeIndex = Unity.Entities.TypeManager.GetTypeIndex(); - static string StructuralChanges_GetTypeIndices(LambdaJobDescription description) => - description.LambdaParameters.Select(param => param.StructuralChanges_GetTypeIndex()) - .SeparateByNewLine(); - - // var rotationOriginal = _rotationLookup[entity]; var rotation = rotationOriginal; - static string StructuralChanges_ReadLambdaParams(LambdaJobDescription description) => - description.LambdaParameters.Select(param => param.StructuralChanges_ReadLambdaParam()) - .SeparateByNewLine(); - - // UnsafeUnsafeWriteComponentData(__this.EntityManager, entity, rotationTypeIndex, ref rotation, ref T originalrotation);"; - static string StructuralChanges_WriteBackLambdaParams(LambdaJobDescription description) => - description.LambdaParameters.Select(param => param.StructuralChanges_WriteBackLambdaParam()) - .SeparateByNewLine(); - - string StructSystemFields() => - description.NeedsTimeData ? "public global::Unity.Core.TimeData __Time;" : string.Empty; - - // public [ReadOnly] CapturedFieldType capturedFieldName; - // Need to also declare these for variables used by local methods - string CapturedVariableFields() - { - static string FieldForCapturedVariable(LambdaCapturedVariableDescription variable) => - $@"{variable.Attributes.JoinAttributes()}public {variable.Symbol.GetSymbolType().ToFullName()} {variable.VariableFieldName};"; - - return description.VariablesCaptured.Select(FieldForCapturedVariable).SeparateByNewLine(); - } - - // public ComponentTypeHandle _rotationTypeAccessor; - string TypeHandleFields() => description.LambdaParameters.Select(param => param.FieldInGeneratedJobChunkType()) - .SeparateByNewLine(); - - // public Unity.Entities.ComponentLookup _rotationLookup; - string AdditionalDataLookupFields() => - description.AdditionalFields - .Select(dataLookupField => dataLookupField.ToFieldDeclaration().ToString()) - .SeparateByNewLine(); - - // void OriginalLambdaBody(ref ComponentType1 component1, in ComponentType2 component2) {}"; - string OriginalLambdaBody() => $@" - {"[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]".EmitIfTrue(description.Burst.IsEnabled)} - void OriginalLambdaBody({description.LambdaParameters.Select(param => param.LambdaBodyMethodParameter(description.Burst.IsEnabled)).SeparateByComma()}) {{}} - {TypeCreationHelpers.GeneratedLineTriviaToGeneratedSource}"; - - // OriginalLambdaBody(ref Unity.Collections.LowLevel.Unsafe.UnsafeUtility.AsRef(componentArray1 + i), *(componentArray2 + i)); - string PerformLambda() - { - var result = string.Empty; - - result += description.LambdaParameters.Select(param => param.LambdaBodyParameterSetup()).SeparateBySemicolonAndNewLine(); - - if (description.WithStructuralChanges) - result += - $@"OriginalLambdaBody({description.LambdaParameters.Select(param => param.StructuralChanges_LambdaBodyParameter()) - .SeparateByCommaAndNewLine()});"; - else - result += - $@"OriginalLambdaBody({description.LambdaParameters.Select(param => param.LambdaBodyParameter()) - .SeparateByCommaAndNewLine()});"; - - return result; - } - - string ExecuteMethodForJob() => - $@" - public void Execute() - {{ - {PerformLambda()} - }}"; - - /* - string ExecuteMethodDefault() => $@" - public void Execute(Unity.Entities.ArchetypeChunk chunk, int batchIndex) - {{ - {GetChunkNativeArrays(description)} - - int count = chunk.Count; - for (int entityIndex = 0; entityIndex != count; entityIndex++) - {{ - {PerformLambda()} - }} - }}"; - */ - - string ExecuteMethodDefault() => $@" - [global::System.Runtime.CompilerServices.CompilerGenerated] - public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchIndex, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask) - {{ - {GetChunkNativeArrays(description)} - int chunkEntityCount = chunk.Count; - {"int matchingEntityCount = 0;".EmitIfTrue(description.NeedsEntityInQueryIndex)} - if (!useEnabledMask) - {{ - for(var entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex) - {{ - {"var entityInQueryIndex = __ChunkBaseEntityIndices[batchIndex] + matchingEntityCount++;".EmitIfTrue(description.NeedsEntityInQueryIndex)} - {PerformLambda()} - }} - }} - else - {{ - int edgeCount = global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong0 ^ (chunkEnabledMask.ULong0 << 1)) + - global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong1 ^ (chunkEnabledMask.ULong1 << 1)) - 1; - bool useRanges = edgeCount <= 4; - if (useRanges) - {{ - int entityIndex = 0; - int batchEndIndex = 0; - while (global::Unity.Entities.EnabledBitUtility.TryGetNextRange(chunkEnabledMask, batchEndIndex, out entityIndex, out batchEndIndex)) - {{ - while (entityIndex < batchEndIndex) - {{ - {"var entityInQueryIndex = __ChunkBaseEntityIndices[batchIndex] + matchingEntityCount++;".EmitIfTrue(description.NeedsEntityInQueryIndex)} - {PerformLambda()} - entityIndex++; - }} - }} - }} - else - {{ - ulong mask64 = chunkEnabledMask.ULong0; - int count = global::Unity.Mathematics.math.min(64, chunkEntityCount); - for (var entityIndex = 0; entityIndex < count; ++entityIndex) - {{ - if ((mask64 & 1) != 0) - {{ - {"var entityInQueryIndex = __ChunkBaseEntityIndices[batchIndex] + matchingEntityCount++;".EmitIfTrue(description.NeedsEntityInQueryIndex)} - {PerformLambda()} - }} - mask64 >>= 1; - }} - mask64 = chunkEnabledMask.ULong1; - for (var entityIndex = 64; entityIndex < chunkEntityCount; ++entityIndex) - {{ - if ((mask64 & 1) != 0) - {{ - {"var entityInQueryIndex = __ChunkBaseEntityIndices[batchIndex] + matchingEntityCount++;".EmitIfTrue(description.NeedsEntityInQueryIndex)} - {PerformLambda()} - }} - mask64 >>= 1; - }} - }} - }} - }}"; - - /* - string ExecuteMethodWithEntityInQueryIndex() => - $@" - public void Execute(in Unity.Entities.ArchetypeChunk chunk, - int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask) - public void Execute(Unity.Entities.ArchetypeChunk chunk, int batchIndex, int indexOfFirstEntityInQuery) - {{ - {GetChunkNativeArrays(description)} - - int count = chunk.Count; - for (int entityIndex = 0; entityIndex != count; entityIndex++) - {{ - int entityInQueryIndex = indexOfFirstEntityInQuery + entityIndex; - {PerformLambda()} - }} - }}"; - */ - - string ExecuteMethodForStructuralChanges() => - $@" - public void RunWithStructuralChange(global::Unity.Entities.EntityQuery query) - {{ - {TypeCreationHelpers.GeneratedLineTriviaToGeneratedSource} - var mask = query.GetEntityQueryMask(); - global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeCreateGatherEntitiesResult(ref query, out var gatherEntitiesResult); - {StructuralChanges_GetTypeIndices(description)} - - try - {{ - int entityCount = gatherEntitiesResult.EntityCount; - for (int entityIndex = 0; entityIndex != entityCount; entityIndex++) - {{ - var entity = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetEntityFromGatheredEntities(ref gatherEntitiesResult, entityIndex); - if (mask.MatchesIgnoreFilter(entity)) - {{ - {StructuralChanges_ReadLambdaParams(description)} - {UpdateComponentLookupAtInnerIteration(description)} - {PerformLambda()} - {StructuralChanges_WriteBackLambdaParams(description)} - }} - }} - }} - finally - {{ - global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeReleaseGatheredEntities(ref query, ref gatherEntitiesResult); - }} - }}"; - - string ExecuteMethodForStructuralChangesWithEntities() => - $@" - public void RunWithStructuralChange(global::Unity.Entities.EntityQuery query, global::Unity.Collections.NativeArray withEntities) - {{ - {TypeCreationHelpers.GeneratedLineTriviaToGeneratedSource} - var mask = query.GetEntityQueryMask(); - {StructuralChanges_GetTypeIndices(description)} - - int entityCount = withEntities.Length; - for (int entityIndex = 0; entityIndex != entityCount; entityIndex++) - {{ - var entity = withEntities[entityIndex]; - if (mask.MatchesIgnoreFilter(entity)) - {{ - {StructuralChanges_ReadLambdaParams(description)} - {UpdateComponentLookupAtInnerIteration(description)} - {PerformLambda()} - {StructuralChanges_WriteBackLambdaParams(description)} - }} - }} - }}"; - - string ExecuteMethod() - { - if (description.LambdaJobKind == LambdaJobKind.Job) - return ExecuteMethodForJob(); - if (description.WithStructuralChanges) - return ExecuteMethodForStructuralChanges(); - if (description.WithStructuralChanges && description.WithFilterEntityArray != null) - return ExecuteMethodForStructuralChangesWithEntities(); - return ExecuteMethodDefault(); - } - - string DisposeOnCompletionMethod() - { - if (!description.DisposeOnJobCompletionVariables.Any()) - return string.Empty; - - var allDisposableFieldsAndChildren = new List(); - foreach (var variable in description.DisposeOnJobCompletionVariables) - allDisposableFieldsAndChildren.AddRange( - variable.NamesOfAllDisposableMembersIncludingOurselves()); - - return description.Schedule.Mode switch - { - ScheduleMode.Run => - $@" - public void DisposeOnCompletion() - {{ - {allDisposableFieldsAndChildren.Select(disposable => $"{disposable}.Dispose();").SeparateByNewLine()} - }}", - - _ => $@" - public global::Unity.Jobs.JobHandle DisposeOnCompletion(global::Unity.Jobs.JobHandle jobHandle) - {{ - {allDisposableFieldsAndChildren.Select(disposable => $"jobHandle = {disposable}.Dispose(jobHandle);").SeparateByNewLine()} - return jobHandle; - }}" - }; - } - - string JobInterface() - { - var jobInterface = ""; - if (description.LambdaJobKind == LambdaJobKind.Job) - jobInterface = " : global::Unity.Jobs.IJob"; - else if (!description.WithStructuralChanges) - jobInterface = " : global::Unity.Entities.IJobChunk"; - - if (!string.IsNullOrEmpty(jobInterface) && description.IsForDOTSRuntime) - jobInterface += ", global::Unity.Jobs.IJobBase"; - - return jobInterface; - } - - static string RunWithoutJobSystemMethod(LambdaJobDescription description) - { - switch (description.LambdaJobKind) - { - case LambdaJobKind.Entities: - { - var jobInterfaceType = "global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface"; - - return - description.WithFilterEntityArray != null - ? $@" - {Common.BurstCompileAttribute(description)} - {Common.MonoPInvokeCallbackAttributeAttribute(description)} - public static void RunWithoutJobSystem(ref global::Unity.Entities.EntityQuery query, global::System.IntPtr limitToEntityArrayPtr, int limitToEntityArrayLength, global::System.IntPtr jobPtr) - {{ - {EnterTempMemoryScope(description)} - try - {{ - {jobInterfaceType}.RunWithoutJobsInternal(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeAsRef<{description.JobStructName}>(jobPtr), ref query, limitToEntityArrayPtr, limitToEntityArrayLength); - }} - finally - {{ - {ExitTempMemoryScope(description)} - }} - }}" - : $@" - {Common.BurstCompileAttribute(description)} - {Common.MonoPInvokeCallbackAttributeAttribute(description)} - public static void RunWithoutJobSystem(ref global::Unity.Entities.EntityQuery query, global::System.IntPtr jobPtr) - {{ - {EnterTempMemoryScope(description)} - try - {{ - {jobInterfaceType}.RunWithoutJobsInternal(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeAsRef<{description.JobStructName}>(jobPtr), ref query); - }} - finally - {{ - {ExitTempMemoryScope(description)} - }} - }}"; - } - - default: - return - $@" - {Common.BurstCompileAttribute(description)} - {Common.MonoPInvokeCallbackAttributeAttribute(description)} - public static void RunWithoutJobSystem(global::System.IntPtr jobPtr) - {{ - {EnterTempMemoryScope(description)} - try - {{ - global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeAsRef<{description.JobStructName}>(jobPtr).Execute(); - }} - finally - {{ - {ExitTempMemoryScope(description)} - }} - }}"; - } - } - - static string EnterTempMemoryScope(LambdaJobDescription description) - { - return !description.IsForDOTSRuntime ? "" : "global::Unity.Runtime.TempMemoryScope.EnterScope();"; - } - - static string ExitTempMemoryScope(LambdaJobDescription description) - { - return !description.IsForDOTSRuntime ? "" : "global::Unity.Runtime.TempMemoryScope.ExitScope();"; - } - - static string RunWithoutJobSystemDelegateFields(LambdaJobDescription description) - { - var delegateName = description.LambdaJobKind switch - { - LambdaJobKind.Entities when description.WithFilterEntityArray != null => "global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegateLimitEntities", - LambdaJobKind.Entities => "global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate", - LambdaJobKind.Job => "global::Unity.Entities.Internal.InternalCompilerInterface.JobRunWithoutJobSystemDelegate", - _ => throw new ArgumentOutOfRangeException() - }; - - var fieldDeclaration = string.Empty; - - /* TODO: Once Burst 1.6 lands in Entities, we should be able to just run through .Invoke everywhere as Burst will do IL patching to remove - the performance issues around calling .Invoke directly. */ - fieldDeclaration = $"internal static {delegateName} FunctionPtrFieldNoBurst;"; - if (description.Burst.IsEnabled) - fieldDeclaration += $"internal static {delegateName} FunctionPtrFieldBurst;"; - - return fieldDeclaration; - } - - static string IJobBaseMethods(LambdaJobDescription description) - { - return @" - public void PrepareJobAtExecuteTimeFn_Gen(int jobIndex, global::System.IntPtr localNodes) - { - throw new global::System.NotImplementedException(); - } - - public void CleanupJobAfterExecuteTimeFn_Gen() - { - throw new global::System.NotImplementedException(); - } - - public void CleanupJobFn_Gen() - { - throw new global::System.NotImplementedException(); - } - - public global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.ManagedJobForEachDelegate GetExecuteMethod_Gen() - { - throw new global::System.NotImplementedException(); - } - - public int GetUnmanagedJobSize_Gen() - { - throw new global::System.NotImplementedException(); - } - - public global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.ManagedJobMarshalDelegate GetMarshalToBurstMethod_Gen() - { - throw new global::System.NotImplementedException(); - } - - public global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.ManagedJobMarshalDelegate GetMarshalFromBurstMethod_Gen() - { - throw new global::System.NotImplementedException(); - } - - public int IsBursted_Gen() - { - throw new global::System.NotImplementedException(); - }" + - (description.SafetyChecksEnabled ? @" - public int PrepareJobAtPreScheduleTimeFn_Gen(ref global::Unity.Development.JobsDebugger.DependencyValidator data, ref global::Unity.Jobs.JobHandle dependsOn, global::System.IntPtr deferredSafety) - { - throw new global::System.NotImplementedException(); - } - - public void PrepareJobAtPostScheduleTimeFn_Gen(ref global::Unity.Development.JobsDebugger.DependencyValidator data, ref global::Unity.Jobs.JobHandle scheduledJob) - { - throw new global::System.NotImplementedException(); - } - - public void PatchMinMax_Gen(global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.MinMax param) - { - throw new global::System.NotImplementedException(); - } - - public int GetSafetyFieldCount() - { - throw new global::System.NotImplementedException(); - }" : "") + - (description.SafetyChecksEnabled || description.DOTSRuntimeProfilerEnabled ? @" - public int GetJobNameIndex_Gen() - { - throw new global::System.NotImplementedException(); - }" : ""); - } - - static string GenerateProfilerMarker(LambdaJobDescription description) - { - if (!description.ProfilerEnabled || description.IsForDOTSRuntime) - return ""; - string marker = "public static readonly global::Unity.Profiling.ProfilerMarker s_ProfilerMarker = new global::Unity.Profiling.ProfilerMarker("; - if (description.Schedule.Mode == ScheduleMode.Run) - { - if (description.Burst.IsEnabled) - marker += - "new global::Unity.Profiling.ProfilerCategory(\"Burst\", global::Unity.Profiling.ProfilerCategoryColor.BurstJobs), "; - marker += "\""; - marker += description.Name; - marker += $"\");{Environment.NewLine}"; - } - else - { - marker += "\""; - marker += description.Name; - if (description.Schedule.Mode == ScheduleMode.Schedule) - marker += ".Schedule"; - else - marker += ".ScheduleParallel"; - marker += $"\");{Environment.NewLine}"; - } - - return marker; - } - } - - public static MethodDeclarationSyntax LambdaBodyMethodFor(LambdaJobDescription description) - { - var template = $@"{(description.NeedsUnsafe ? "unsafe " : string.Empty)} void {description.LambdaBodyMethodName}({description.LambdaParameters.Select( - param => param.LambdaBodyMethodParameter(description.Burst.IsEnabled)).SeparateByComma()}) - {description.RewrittenLambdaBody.ToString()}"; - return (MethodDeclarationSyntax) SyntaxFactory.ParseMemberDeclaration(template); - } - - public static MethodDeclarationSyntax CreateExecuteMethod(LambdaJobDescription description) - { - var temporaryEcbCreation = CreateTemporaryEcb(description); - - bool addJobHandleForProducer = description.EntityCommandBufferParameter is {Playback: {IsImmediate: false}} - && description.EntityCommandBufferParameter.Playback.ScheduleMode != ScheduleMode.Run; - - var emitProfilerMarker = description.ProfilerEnabled && !description.IsForDOTSRuntime; - - var template = - $@"{(description.NeedsUnsafe ? "unsafe " : string.Empty)} {ReturnType()} {description.ExecuteInSystemMethodName}({ExecuteMethodParams()}) - {{ - {ComponentTypeHandleFieldUpdate()} - {(description.WithStructuralChanges ? "" : UpdateComponentLookupAtScheduleSite(description) /* .wsc done per entity instead */)} - {temporaryEcbCreation.Code} - var __job = new {description.JobStructName} - {{ - {JobStructFieldAssignments().SeparateByCommaAndNewLine()} - }}; - {Common.SharedComponentFilterInvocations(description)} - {"global::Unity.Jobs.JobHandle __jobHandle;".EmitIfTrue(description.Schedule.DependencyArgument != null)} - {CalculateChunkBaseEntityIndices()} - {$"using ({description.JobStructName}.s_ProfilerMarker.Auto()) {{".EmitIfTrue(emitProfilerMarker)} - {ScheduleInvocation()} - {"}".EmitIfTrue(emitProfilerMarker)} - {Common.ResetSharedComponentFilter(description)} - {DisposeOnCompletionInvocation()} - {$"{description.EntityCommandBufferParameter?.GeneratedEcbFieldNameInSystemBaseType}.AddJobHandleForProducer(Dependency);".EmitIfTrue(addJobHandleForProducer)} - {$"{TemporaryJobEntityCommandBufferVariableName}.Playback(EntityManager);".EmitIfTrue(temporaryEcbCreation.Success)} - {$"{TemporaryJobEntityCommandBufferVariableName}.Dispose();".EmitIfTrue(temporaryEcbCreation.Success)} - {WriteBackCapturedVariablesAssignments()} - {"return __jobHandle;".EmitIfTrue(description.Schedule.DependencyArgument != null)} - }}"; - - string CalculateChunkBaseEntityIndices() - { - if (!description.NeedsEntityInQueryIndex) - return string.Empty; - - if (description.Schedule.Mode == ScheduleMode.Run) - return $"__job.__ChunkBaseEntityIndices = {description.EntityQueryFieldName}.CalculateBaseEntityIndexArray(this.CheckedStateRef.WorldUpdateAllocator);"; - - if (description.Schedule.DependencyArgument != null) - return @$" - global::Unity.Collections.NativeArray {description.ChunkBaseEntityIndexFieldName} = {description.EntityQueryFieldName}.CalculateBaseEntityIndexArrayAsync(this.CheckedStateRef.WorldUpdateAllocator, __inputDependency, out __inputDependency); - __job.__ChunkBaseEntityIndices = {description.ChunkBaseEntityIndexFieldName};"; - return @$" - global::Unity.Jobs.JobHandle outHandle; - global::Unity.Collections.NativeArray {description.ChunkBaseEntityIndexFieldName} = {description.EntityQueryFieldName}.CalculateBaseEntityIndexArrayAsync(this.CheckedStateRef.WorldUpdateAllocator, Dependency, out outHandle); - __job.__ChunkBaseEntityIndices = {description.ChunkBaseEntityIndexFieldName}; - Dependency = outHandle;"; - } - - return (MethodDeclarationSyntax) SyntaxFactory.ParseMemberDeclaration(template); - - string ExecuteMethodParams() - { - string ParamsForCapturedVariable(LambdaCapturedVariableDescription variable) - { - return - description.Schedule.Mode == ScheduleMode.Run && variable.IsWritable - ? $@"ref {variable.Type.ToFullName()} {variable.Symbol.Name}" - : $@"{variable.Type.ToFullName()} {variable.Symbol.Name}"; - } - - var paramStrings = new List(); - paramStrings.AddRange(description.VariablesCaptured.Where(variable => !variable.IsThis) - .Select(ParamsForCapturedVariable)); - if (description.Schedule.DependencyArgument != null) - { - paramStrings.Add(@"global::Unity.Jobs.JobHandle __inputDependency"); - } - - if (description.WithFilterEntityArray != null) - { - paramStrings.Add($@"global::Unity.Collections.NativeArray __entityArray"); - } - - foreach (var argument in description.AdditionalVariablesCapturedForScheduling) - paramStrings.Add($@"{argument.Symbol.GetSymbolType().ToFullName()} {argument.Name}"); - - return paramStrings.Distinct().SeparateByComma(); - } - - IEnumerable JobStructFieldAssignments() - { - foreach (var capturedVariable in description.VariablesCaptured) - yield return $@"{capturedVariable.VariableFieldName} = {capturedVariable.OriginalVariableName}"; - - if (!description.WithStructuralChanges) - foreach (var param in description.LambdaParameters) - yield return $"{param.FieldAssignmentInGeneratedJobChunkType(description)}"; - - foreach (var field in description.AdditionalFields) - yield return field.JobStructAssign(); - - if (description.NeedsTimeData) - yield return $"__Time = this.CheckedStateRef.WorldUnmanaged.Time"; - } - - string ComponentTypeHandleFieldUpdate() => - description - .LambdaParameters - .OfType() - .Select(c => c.FormatUpdateInvocation(description)) - .SeparateByNewLine(); - - string ScheduleJobInvocation() - { - switch (description.Schedule.Mode) - { - case ScheduleMode.Run: - { - if (description.Burst.IsEnabled) - { - return $@" - this.CheckedStateRef.CompleteDependency(); - var __functionPointer = global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobCompilerEnabled - ? {description.JobStructName}.FunctionPtrFieldBurst - : {description.JobStructName}.FunctionPtrFieldNoBurst; - global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeRunIJob(ref __job, __functionPointer);"; - } - return $@" - this.CheckedStateRef.CompleteDependency(); - __job.Execute();"; - } - - case ScheduleMode.Schedule: - { - return - description.Schedule.DependencyArgument != null - ? "__jobHandle = global::Unity.Jobs.IJobExtensions.Schedule(__job, __inputDependency);" - : "this.CheckedStateRef.Dependency = global::Unity.Jobs.IJobExtensions.Schedule(__job, this.CheckedStateRef.Dependency);"; - } - } - - throw new InvalidOperationException( - "Can't create ScheduleJobInvocation for invalid lambda description"); - } - - string ScheduleEntitiesInvocation() - { - string EntityQueryParameter() => $"{description.EntityQueryFieldName}"; - - string OutputOptionalEntityArrayParameter(ArgumentSyntax entityArray) - { - if (entityArray != null) return ", __entityArray"; - - // Special case when we have WithScheduleGranularity but no entity array - // the only ScheduleParallel signature available with a granularity parameter we can use here is - // ScheduleParallel(job, query, granularity, entity array) - // to call it, entity array must be set to "default" - return description.WithScheduleGranularityArgumentSyntaxes.Count > 0 ? ", default" : ""; - } - - string OutputOptionalGranularityParameter() => - description.WithScheduleGranularityArgumentSyntaxes.Count > 0 ? $", {description.WithScheduleGranularityArgumentSyntaxes.ElementAt(0)}" : ""; - - string OutputOptionalChunkBaseIndexArrayParameter() - { - // If the job requires entityInQueryIndex, the array of per-chunk base entity indices must be passed as - // the last parameter of ScheduleParallel() in order to call JobsUtility.PatchBufferMinMaxRanges() - bool needsEntityInQueryIndex = description.NeedsEntityInQueryIndex; - bool isScheduleParallel = (description.Schedule.Mode == ScheduleMode.ScheduleParallel); - return (needsEntityInQueryIndex && isScheduleParallel) ? $", {description.ChunkBaseEntityIndexFieldName}" : ""; - } - - // Certain schedule paths require .Schedule/.Run calls that aren't in the IJobChunk public API, - // and only appear in InternalCompilerInterface - string JobChunkExtensionType() => "global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface"; - - switch (description.Schedule.Mode) - { - case ScheduleMode.Run: - { - if (description.WithStructuralChanges) - { - var entityArray = - ", __entityArray".EmitIfTrue(description.WithFilterEntityArray != null); - - return $@" - if(!{description.EntityQueryFieldName}.IsEmptyIgnoreFilter) - {{ - this.CheckedStateRef.CompleteDependency(); - __job.RunWithStructuralChange({description.EntityQueryFieldName}{entityArray}); - }}"; - } - - if (description.Burst.IsEnabled) - { - var scheduleMethod = "UnsafeRunJobChunk"; - - var additionalSetup = @$"var __functionPointer = global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobCompilerEnabled - ? {description.JobStructName}.FunctionPtrFieldBurst - : {description.JobStructName}.FunctionPtrFieldNoBurst;"; - var scheduleArguments = (description.WithFilterEntityArray != null) - ? $"ref __job, {description.EntityQueryFieldName}, global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetEntityArrayIntPtr(__entityArray), __entityArray.Length, __functionPointer" - : $"ref __job, {description.EntityQueryFieldName}, __functionPointer"; - - return $@" - if(!{description.EntityQueryFieldName}.IsEmptyIgnoreFilter) - {{ - this.CheckedStateRef.CompleteDependency(); - {additionalSetup} - global::Unity.Entities.Internal.InternalCompilerInterface.{scheduleMethod}({scheduleArguments}); - }}"; - } - - return $@" - if(!{description.EntityQueryFieldName}.IsEmptyIgnoreFilter) - {{ - this.CheckedStateRef.CompleteDependency(); - {JobChunkExtensionType()}.RunByRefWithoutJobs(ref __job, {description.EntityQueryFieldName}); - }}"; - } - - case ScheduleMode.Schedule: - { - return - description.Schedule.DependencyArgument != null - ? $@"__jobHandle = {JobChunkExtensionType()}.Schedule(__job, {EntityQueryParameter()}{OutputOptionalEntityArrayParameter(description.WithFilterEntityArray)}, __inputDependency);" - : $@"this.CheckedStateRef.Dependency = {JobChunkExtensionType()}.Schedule(__job, {EntityQueryParameter()}{OutputOptionalEntityArrayParameter(description.WithFilterEntityArray)}, this.CheckedStateRef.Dependency);"; - } - - case ScheduleMode.ScheduleParallel: - { - return - description.Schedule.DependencyArgument != null - ? $@"__jobHandle = {JobChunkExtensionType()}.ScheduleParallel(__job, {EntityQueryParameter()}{OutputOptionalGranularityParameter()}{OutputOptionalEntityArrayParameter(description.WithFilterEntityArray)}, __inputDependency{OutputOptionalChunkBaseIndexArrayParameter()});" - : $@"this.CheckedStateRef.Dependency = {JobChunkExtensionType()}.ScheduleParallel(__job, {EntityQueryParameter()}{OutputOptionalGranularityParameter()}{OutputOptionalEntityArrayParameter(description.WithFilterEntityArray)}, this.CheckedStateRef.Dependency{OutputOptionalChunkBaseIndexArrayParameter()});"; - } - } - - throw new InvalidOperationException("Can't create ScheduleJobInvocation for invalid lambda description"); - } - - string ScheduleInvocation() - { - return description.LambdaJobKind == LambdaJobKind.Entities - ? ScheduleEntitiesInvocation() - : ScheduleJobInvocation(); - } - - string WriteBackCapturedVariablesAssignments() - { - if (description.Schedule.Mode != ScheduleMode.Run) - return string.Empty; - - return - description - .VariablesCaptured - .Where(variable => !variable.IsThis && variable.IsWritable) - .Select(variable => $@"{variable.OriginalVariableName} = __job.{variable.VariableFieldName};") - .SeparateByNewLine(); - } - - string DisposeOnCompletionInvocation() - { - if (!description.DisposeOnJobCompletionVariables.Any()) - return string.Empty; - if (description.Schedule.Mode == ScheduleMode.Run) - return @"__job.DisposeOnCompletion();"; - if (description.Schedule.DependencyArgument != null) - return @"__jobHandle = __job.DisposeOnCompletion(__jobHandle);"; - return $@"this.CheckedStateRef.Dependency = __job.DisposeOnCompletion(this.CheckedStateRef.Dependency);"; - } - - string ReturnType() => - description.Schedule.DependencyArgument != null ? "global::Unity.Jobs.JobHandle" : "void"; - } - - static string UpdateComponentLookupAtScheduleSite(LambdaJobDescription description) => - description.AdditionalFields - .Select(c => c.FormatUpdateInvocation()) - .SeparateByNewLine(); - - static string UpdateComponentLookupAtInnerIteration(LambdaJobDescription description) => - description.AdditionalFields - .Select(c => $"{c.FieldName}.Update(__this);") - .SeparateByNewLine(); - - static (bool Success, string Code) CreateTemporaryEcb(LambdaJobDescription description) - { - if (description.EntityCommandBufferParameter == null) - return (false, string.Empty); - return - description.EntityCommandBufferParameter.Playback.IsImmediate - ? (true, $"global::Unity.Entities.EntityCommandBuffer {TemporaryJobEntityCommandBufferVariableName} = new global::Unity.Entities.EntityCommandBuffer(this.World.UpdateAllocator.ToAllocator);") - : (false, string.Empty); - } - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/Enums.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/Enums.cs index e83c9a5..e5fd2b0 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/Enums.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/Enums.cs @@ -1,25 +1,24 @@ -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +public enum ScheduleMode { - public enum ScheduleMode - { - ScheduleParallel, - Schedule, - Run - } + ScheduleParallel, + Schedule, + Run +} - public enum BurstFloatMode - { - Default, - Strict, - Deterministic, - Fast, - } +public enum BurstFloatMode +{ + Default, + Strict, + Deterministic, + Fast, +} - public enum BurstFloatPrecision - { - Standard, - High, - Medium, - Low, - } +public enum BurstFloatPrecision +{ + Standard, + High, + Medium, + Low, } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ExecuteMethodWriter.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ExecuteMethodWriter.cs new file mode 100644 index 0000000..9cdcc34 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ExecuteMethodWriter.cs @@ -0,0 +1,306 @@ +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Linq; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +public struct ExecuteMethodWriter : IMemberWriter +{ + const string TemporaryJobEntityCommandBufferVariableName = "__tempJobEcb"; + + public LambdaJobDescription LambdaJobDescription; + + IEnumerable GetJobStructFieldAssignments() + { + foreach (var capturedVariable in LambdaJobDescription.VariablesCaptured) + yield return $@"{capturedVariable.VariableFieldName} = {capturedVariable.OriginalVariableName}"; + + if (!LambdaJobDescription.WithStructuralChanges) + foreach (var param in LambdaJobDescription.LambdaParameters) + { + var assignment = param.FieldAssignmentInGeneratedJobChunkType(); + if (assignment != null) + yield return $"{assignment}"; + } + + foreach (var field in LambdaJobDescription.AdditionalFields) + yield return field.JobStructAssign(); + + if (LambdaJobDescription.NeedsTimeData) + yield return "__Time = this.CheckedStateRef.WorldUnmanaged.Time"; + } + + public void WriteTo(IndentedTextWriter writer) + { + if (LambdaJobDescription.NeedsUnsafe) + writer.Write("unsafe "); + + writer.Write(LambdaJobDescription.Schedule.DependencyArgument != null ? "Unity.Jobs.JobHandle " : "void "); + writer.Write(LambdaJobDescription.ExecuteInSystemMethodName); + writer.Write("("); + writer.Write(GetExecuteMethodParams().SeparateByComma()); + writer.WriteLine(")"); + writer.WriteLine("{"); + writer.Indent++; + + foreach (var p in LambdaJobDescription.LambdaParameters) + { + if (p is IParamRequireUpdate requireUpdate) + writer.WriteLine($"{requireUpdate.FormatUpdateInvocation(LambdaJobDescription)}"); + } + + if (!LambdaJobDescription.WithStructuralChanges) + { + foreach (var fieldDescription in LambdaJobDescription.AdditionalFields) + writer.WriteLine($"{fieldDescription.FormatUpdateInvocation()};"); + } + + if (LambdaJobDescription.EntityCommandBufferParameter is { Playback: { IsImmediate: true } }) + writer.WriteLine($"global::Unity.Entities.EntityCommandBuffer {TemporaryJobEntityCommandBufferVariableName} = new global::Unity.Entities.EntityCommandBuffer(this.World.UpdateAllocator.ToAllocator);"); + + writer.WriteLine($"var __job = new {LambdaJobDescription.JobStructName}"); + writer.WriteLine("{"); + writer.Indent++; + + var assignments = GetJobStructFieldAssignments().ToArray(); + for (var index = 0; index < assignments.Length; index++) + { + var assignment = assignments[index]; + writer.Write(assignment); + if (index < assignments.Length - 1) + { + writer.Write(","); + writer.WriteLine(); + } + } + writer.Indent--; + writer.WriteLine(); + writer.WriteLine("};"); + + foreach (var arg in LambdaJobDescription.WithSharedComponentFilterArgumentSyntaxes) + writer.WriteLine($@"{LambdaJobDescription.EntityQueryFieldName}.SetSharedComponentFilter({arg});"); + + if (LambdaJobDescription.Schedule.DependencyArgument != null) + writer.WriteLine("Unity.Jobs.JobHandle __jobHandle;"); + + writer.WriteLine(CalculateChunkBaseEntityIndices().SeparateByNewLine()); + + var profilerEnabled = LambdaJobDescription.ProfilerEnabled && !LambdaJobDescription.IsForDOTSRuntime; + if (profilerEnabled) + { + writer.WriteLine($"using ({LambdaJobDescription.JobStructName}.s_ProfilerMarker.Auto())"); + writer.WriteLine("{"); + writer.Indent++; + } + + WriteScheduleInvocation(writer); + + if (profilerEnabled) + { + writer.Indent--; + writer.WriteLine("}"); + } + + if (LambdaJobDescription.WithSharedComponentFilterArgumentSyntaxes.Count > 0) + writer.WriteLine( $@"{LambdaJobDescription.EntityQueryFieldName}.ResetFilter();"); + + if (LambdaJobDescription.DisposeOnJobCompletionVariables.Count > 0) + { + if (LambdaJobDescription.Schedule.Mode == ScheduleMode.Run) + writer.WriteLine("__job.DisposeOnCompletion();"); + else if (LambdaJobDescription.Schedule.DependencyArgument != null) + writer.WriteLine(@"__jobHandle = __job.DisposeOnCompletion(__jobHandle);"); + else + writer.WriteLine(@"this.CheckedStateRef.Dependency = __job.DisposeOnCompletion(this.CheckedStateRef.Dependency);"); + } + bool addJobHandleForProducer = LambdaJobDescription.EntityCommandBufferParameter is {Playback: {IsImmediate: false}} + && LambdaJobDescription.EntityCommandBufferParameter.Playback.ScheduleMode != ScheduleMode.Run; + if (addJobHandleForProducer) + writer.WriteLine($"{LambdaJobDescription.EntityCommandBufferParameter.GeneratedEcbFieldNameInSystemBaseType}.AddJobHandleForProducer(Dependency);"); + + var createTempEcb = CreateTemporaryEcb(); + if (createTempEcb != null) + { + writer.WriteLine($"{TemporaryJobEntityCommandBufferVariableName}.Playback(EntityManager);"); + writer.WriteLine($"{TemporaryJobEntityCommandBufferVariableName}.Dispose();"); + } + + if (LambdaJobDescription.Schedule.Mode == ScheduleMode.Run) + { + foreach (var v in LambdaJobDescription.VariablesCaptured) + { + if (v is {IsThis: false, IsWritable: true}) + writer.WriteLine( $@"{v.OriginalVariableName} = __job.{v.VariableFieldName};"); + } + } + + if (LambdaJobDescription.Schedule.DependencyArgument != null) + writer.WriteLine("return __jobHandle;"); + + writer.Indent--; + writer.WriteLine("}"); + } + + void WriteScheduleInvocation(IndentedTextWriter writer) + { + if (LambdaJobDescription.LambdaJobKind == LambdaJobKind.Entities) + ScheduleEntitiesInvocation(writer); + else + ScheduleJobInvocation(writer); + } + + void ScheduleEntitiesInvocation(IndentedTextWriter writer) + { + switch (LambdaJobDescription.Schedule.Mode) + { + case ScheduleMode.Run: + { + if (LambdaJobDescription.WithStructuralChanges) + { + writer.WriteLine($"if(!{LambdaJobDescription.EntityQueryFieldName}.IsEmptyIgnoreFilter)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("this.CheckedStateRef.CompleteDependency();"); + writer.WriteLine($"__job.RunWithStructuralChange({LambdaJobDescription.EntityQueryFieldName});"); + writer.Indent--; + writer.WriteLine("}"); + } + else if (LambdaJobDescription.Burst.IsEnabled) + { + writer.WriteLine($"if(!{LambdaJobDescription.EntityQueryFieldName}.IsEmptyIgnoreFilter)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("this.CheckedStateRef.CompleteDependency();"); + writer.WriteLine(@$"var __functionPointer = global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobCompilerEnabled ? {LambdaJobDescription.JobStructName}.FunctionPtrFieldBurst : {LambdaJobDescription.JobStructName}.FunctionPtrFieldNoBurst;"); + writer.WriteLine($"global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeRunJobChunk(ref __job, {LambdaJobDescription.EntityQueryFieldName}, __functionPointer);"); + writer.Indent--; + writer.WriteLine("}"); + } + else + { + writer.WriteLine($"if(!{LambdaJobDescription.EntityQueryFieldName}.IsEmptyIgnoreFilter)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"this.CheckedStateRef.CompleteDependency();"); + writer.WriteLine($"global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.RunByRefWithoutJobs(ref __job, {LambdaJobDescription.EntityQueryFieldName});"); + writer.Indent--; + writer.WriteLine("}"); + } + break; + } + + case ScheduleMode.Schedule: + { + writer.WriteLine(LambdaJobDescription.Schedule.DependencyArgument != null + ? $@"__jobHandle = global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.Schedule(__job, {LambdaJobDescription.EntityQueryFieldName}{(LambdaJobDescription.WithScheduleGranularityArgumentSyntaxes.Count > 0 ? ", default" : "")}, __inputDependency);" + : $@"this.CheckedStateRef.Dependency = global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.Schedule(__job, {LambdaJobDescription.EntityQueryFieldName}{(LambdaJobDescription.WithScheduleGranularityArgumentSyntaxes.Count > 0 ? ", default" : "")}, this.CheckedStateRef.Dependency);"); + break; + } + + case ScheduleMode.ScheduleParallel: + { + writer.WriteLine(LambdaJobDescription.Schedule.DependencyArgument != null + ? $@"__jobHandle = global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.ScheduleParallel(__job, {LambdaJobDescription.EntityQueryFieldName}{(LambdaJobDescription.WithScheduleGranularityArgumentSyntaxes.Count > 0 ? $", {LambdaJobDescription.WithScheduleGranularityArgumentSyntaxes.ElementAt(0)}" : "")}{(LambdaJobDescription.WithScheduleGranularityArgumentSyntaxes.Count > 0 ? ", default" : "")}, __inputDependency{(LambdaJobDescription.NeedsEntityInQueryIndex && LambdaJobDescription.Schedule.Mode == ScheduleMode.ScheduleParallel ? $", {LambdaJobDescription.ChunkBaseEntityIndexFieldName}" : "")});" + : $@"this.CheckedStateRef.Dependency = global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.ScheduleParallel(__job, {LambdaJobDescription.EntityQueryFieldName}{(LambdaJobDescription.WithScheduleGranularityArgumentSyntaxes.Count > 0 ? $", {LambdaJobDescription.WithScheduleGranularityArgumentSyntaxes.ElementAt(0)}" : "")}{(LambdaJobDescription.WithScheduleGranularityArgumentSyntaxes.Count > 0 ? ", default" : "")}, this.CheckedStateRef.Dependency{(LambdaJobDescription.NeedsEntityInQueryIndex && LambdaJobDescription.Schedule.Mode == ScheduleMode.ScheduleParallel ? $", {LambdaJobDescription.ChunkBaseEntityIndexFieldName}" : "")});"); + break; + } + default: + throw new InvalidOperationException("Can't create ScheduleJobInvocation for invalid lambda description"); + } + } + + void ScheduleJobInvocation(IndentedTextWriter writer) + { + switch (LambdaJobDescription.Schedule.Mode) + { + case ScheduleMode.Run: + { + if (LambdaJobDescription.Burst.IsEnabled) + { + writer.WriteLine("this.CheckedStateRef.CompleteDependency();"); + writer.WriteLine( + @$"var __functionPointer = global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobCompilerEnabled ? {LambdaJobDescription.JobStructName}.FunctionPtrFieldBurst : {LambdaJobDescription.JobStructName}.FunctionPtrFieldNoBurst;"); + writer.WriteLine( + $"global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeRunIJob(ref __job, __functionPointer);"); + } + else + { + writer.WriteLine("this.CheckedStateRef.CompleteDependency();"); + writer.WriteLine("__job.Execute();"); + } + + break; + } + + case ScheduleMode.Schedule: + { + writer.WriteLine(LambdaJobDescription.Schedule.DependencyArgument != null + ? "__jobHandle = global::Unity.Jobs.IJobExtensions.Schedule(__job, __inputDependency);" + : "this.CheckedStateRef.Dependency = global::Unity.Jobs.IJobExtensions.Schedule(__job, this.CheckedStateRef.Dependency);"); + break; + } + } + } + + IEnumerable CalculateChunkBaseEntityIndices() + { + if (!LambdaJobDescription.NeedsEntityInQueryIndex) + yield return string.Empty; + + else if (LambdaJobDescription.Schedule.Mode == ScheduleMode.Run) + yield return $"__job.__ChunkBaseEntityIndices = {LambdaJobDescription.EntityQueryFieldName}.CalculateBaseEntityIndexArray(this.CheckedStateRef.WorldUpdateAllocator);"; + + else if (LambdaJobDescription.Schedule.DependencyArgument != null) + { + yield return + @$"global::Unity.Collections.NativeArray {LambdaJobDescription.ChunkBaseEntityIndexFieldName} = {LambdaJobDescription.EntityQueryFieldName}.CalculateBaseEntityIndexArrayAsync(this.CheckedStateRef.WorldUpdateAllocator, __inputDependency, out __inputDependency);"; + yield return $"__job.__ChunkBaseEntityIndices = {LambdaJobDescription.ChunkBaseEntityIndexFieldName};"; + } + else + { + yield return "global::Unity.Jobs.JobHandle outHandle;"; + yield return $"global::Unity.Collections.NativeArray {LambdaJobDescription.ChunkBaseEntityIndexFieldName} = {LambdaJobDescription.EntityQueryFieldName}.CalculateBaseEntityIndexArrayAsync(this.CheckedStateRef.WorldUpdateAllocator, Dependency, out outHandle);"; + yield return $"__job.__ChunkBaseEntityIndices = {LambdaJobDescription.ChunkBaseEntityIndexFieldName};"; + yield return "Dependency = outHandle;"; + } + } + + IEnumerable GetExecuteMethodParams() + { + var distinctParams = new HashSet(); + foreach (var v in LambdaJobDescription.VariablesCaptured) + { + if (!v.IsThis) + { + if (LambdaJobDescription.Schedule.Mode == ScheduleMode.Run && v.IsWritable) + distinctParams.Add($"ref {v.Type.ToFullName()} {v.Symbol.Name}"); + else + distinctParams.Add($"{v.Type.ToFullName()} {v.Symbol.Name}"); + } + } + + if (LambdaJobDescription.Schedule.DependencyArgument != null) + distinctParams.Add("Unity.Jobs.JobHandle __inputDependency"); + + if (LambdaJobDescription.WithFilterEntityArray != null) + distinctParams.Add($"global::Unity.Entities.Entity* {LambdaJobDescription.WithFilterEntityArray}"); + + foreach (var argument in LambdaJobDescription.AdditionalVariablesCapturedForScheduling) + distinctParams.Add($@"{argument.Symbol.GetSymbolType().ToFullName()} {argument.Name}"); + + return distinctParams; + } + + string CreateTemporaryEcb() + { + if (LambdaJobDescription.EntityCommandBufferParameter == null) + return null; + return + LambdaJobDescription.EntityCommandBufferParameter.Playback.IsImmediate + ? $"global::Unity.Entities.EntityCommandBuffer {TemporaryJobEntityCommandBufferVariableName} = new global::Unity.Entities.EntityCommandBuffer(this.World.UpdateAllocator.ToAllocator);" + : null; + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/JobStructWriter.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/JobStructWriter.cs new file mode 100644 index 0000000..b5f88b8 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/JobStructWriter.cs @@ -0,0 +1,618 @@ +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +public struct JobStructWriter : IMemberWriter +{ + public LambdaJobDescription LambdaJobDescription; + + public void WriteTo(IndentedTextWriter writer) + { + writer.WriteLine(TypeCreationHelpers.GeneratedLineTriviaToGeneratedSource); + + // Generate Burst-related attributes + if (LambdaJobDescription.Burst.IsEnabled) + { + writer.WriteLine("[global::Unity.Burst.NoAlias]"); + WriteBurstCompileAttribute(writer); + } + + // Generate job struct + writer.Write(LambdaJobDescription.NeedsUnsafe ? "unsafe " : string.Empty); + writer.Write($"struct {LambdaJobDescription.JobStructName}"); + + // Implement IJob or IJobChunk + bool implementsIJobOrIJobChunk = false; + if (LambdaJobDescription.LambdaJobKind == LambdaJobKind.Job) + { + writer.Write(" : global::Unity.Jobs.IJob"); + implementsIJobOrIJobChunk = true; + } + else if (!LambdaJobDescription.WithStructuralChanges) + { + writer.Write(" : global::Unity.Entities.IJobChunk"); + implementsIJobOrIJobChunk = true; + } + + // Implement IJobBase if needed + if (implementsIJobOrIJobChunk && LambdaJobDescription.IsForDOTSRuntime) + writer.Write(", global::Unity.Jobs.IJobBase"); + + writer.WriteLine(); + writer.WriteLine("{"); + writer.Indent++; + + if (LambdaJobDescription.IsForDOTSRuntime) + { + // Implement IJobBase methods + writer.WriteLine( + "public void PrepareJobAtExecuteTimeFn_Gen(int jobIndex, global::System.IntPtr localNodes)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine("public void CleanupJobAfterExecuteTimeFn_Gen()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine("public void CleanupJobFn_Gen()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine( + "public global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.ManagedJobForEachDelegate GetExecuteMethod_Gen()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine("public int GetUnmanagedJobSize_Gen()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine( + "public global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.ManagedJobMarshalDelegate GetMarshalToBurstMethod_Gen()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine( + "public global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.ManagedJobMarshalDelegate GetMarshalFromBurstMethod_Gen()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine("public int IsBursted_Gen()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine(); + if (LambdaJobDescription.SafetyChecksEnabled) + { + writer.WriteLine( + "public int PrepareJobAtPreScheduleTimeFn_Gen(ref global::Unity.Development.JobsDebugger.DependencyValidator data, ref global::Unity.Jobs.JobHandle dependsOn, global::System.IntPtr deferredSafety)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine( + "public void PrepareJobAtPostScheduleTimeFn_Gen(ref global::Unity.Development.JobsDebugger.DependencyValidator data, ref global::Unity.Jobs.JobHandle scheduledJob)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine( + "public void PatchMinMax_Gen(global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.MinMax param)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine("public int GetSafetyFieldCount()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + } + + if (LambdaJobDescription.SafetyChecksEnabled || LambdaJobDescription.DOTSRuntimeProfilerEnabled) + { + writer.WriteLine("public int GetJobNameIndex_Gen()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + } + } + + if (LambdaJobDescription.NeedsEntityInQueryIndex) + { + writer.WriteLine("[global::Unity.Collections.ReadOnly]"); + writer.WriteLine("public global::Unity.Collections.NativeArray __ChunkBaseEntityIndices;"); + } + + if (LambdaJobDescription.NeedsJobFunctionPointers) + { + // Add functional pointer fields + // TODO: Once Burst 1.6 lands in Entities, we should be able to just run through .Invoke everywhere as Burst will do IL patching to remove + string delegateName = LambdaJobDescription.LambdaJobKind switch + { + LambdaJobKind.Entities => + "global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate", + LambdaJobKind.Job => + "global::Unity.Entities.Internal.InternalCompilerInterface.JobRunWithoutJobSystemDelegate", + _ => throw new ArgumentOutOfRangeException() + }; + + writer.WriteLine($"internal static {delegateName} FunctionPtrFieldNoBurst;"); + if (LambdaJobDescription.Burst.IsEnabled) + writer.WriteLine($"internal static {delegateName} FunctionPtrFieldBurst;"); + } + + if (LambdaJobDescription.NeedsTimeData) + writer.WriteLine("public global::Unity.Core.TimeData __Time;"); + + foreach (var variable in LambdaJobDescription.VariablesCaptured) + { + foreach (var attr in variable.Attributes) writer.WriteLine($"[{attr}]"); + writer.WriteLine( + $"public {variable.Symbol.GetSymbolType().ToFullName()} {variable.VariableFieldName};"); + } + + if (!LambdaJobDescription.WithStructuralChanges) + { + foreach (var p in LambdaJobDescription.LambdaParameters) + writer.WriteLine(p.FieldInGeneratedJobChunkType()); + } + + foreach (var field in LambdaJobDescription.AdditionalFields) + writer.WriteLine(field.ToFieldDeclaration().ToString()); + + if (LambdaJobDescription.ProfilerEnabled && !LambdaJobDescription.IsForDOTSRuntime) + { + writer.Write( + "public static readonly global::Unity.Profiling.ProfilerMarker s_ProfilerMarker = new global::Unity.Profiling.ProfilerMarker("); + + if (LambdaJobDescription.Schedule.Mode == ScheduleMode.Run) + { + if (LambdaJobDescription.Burst.IsEnabled) + writer.Write( + "new global::Unity.Profiling.ProfilerCategory(\"Burst\", global::Unity.Profiling.ProfilerCategoryColor.BurstJobs), "); + + writer.Write("\""); + writer.Write(LambdaJobDescription.Name); + writer.Write("\");"); + writer.WriteLine(); + } + else + { + writer.Write("\""); + writer.Write(LambdaJobDescription.Name); + writer.Write(LambdaJobDescription.Schedule.Mode == ScheduleMode.Schedule ? ".Schedule" : ".ScheduleParallel"); + writer.Write("\");"); + writer.WriteLine(); + } + } + + writer.WriteLine(); + // Generate original lambda body + if (LambdaJobDescription.Burst.IsEnabled) + writer.WriteLine( + "[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + + writer.Write("void OriginalLambdaBody("); + + bool hasPrevParam = false; + foreach (var p in LambdaJobDescription.LambdaParameters) + { + var methodParameter = p.LambdaBodyMethodParameter(LambdaJobDescription.Burst.IsEnabled); + if (methodParameter != null) + { + if (hasPrevParam) + writer.Write(", "); + + writer.Write(methodParameter); + hasPrevParam = true; + } + } + writer.Write(")"); + writer.WriteLine(); + writer.Write(LambdaJobDescription.RewrittenLambdaBody.WithoutPreprocessorTrivia()); + writer.WriteLine(); + + writer.WriteLine($"{TypeCreationHelpers.GeneratedLineTriviaToGeneratedSource}"); + WriteExecuteMethod(writer); + + if (LambdaJobDescription.DisposeOnJobCompletionVariables.Count > 0) + { + if (LambdaJobDescription.Schedule.Mode == ScheduleMode.Run) + { + writer.WriteLine("public void DisposeOnCompletion()"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var v in LambdaJobDescription.DisposeOnJobCompletionVariables) + foreach (var name in v.NamesOfAllDisposableMembersIncludingOurselves()) + writer.WriteLine($"{name}.Dispose();"); + + writer.Indent--; + writer.WriteLine("}"); + } + else + { + writer.WriteLine( + "public global::Unity.Jobs.JobHandle DisposeOnCompletion(global::Unity.Jobs.JobHandle jobHandle)"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var v in LambdaJobDescription.DisposeOnJobCompletionVariables) + foreach (var name in v.NamesOfAllDisposableMembersIncludingOurselves()) + writer.WriteLine($"jobHandle = {name}.Dispose(jobHandle);"); + + writer.WriteLine("return jobHandle;"); + writer.Indent--; + writer.WriteLine("}"); + } + } + + if (LambdaJobDescription.NeedsJobFunctionPointers) + { + if (LambdaJobDescription.LambdaJobKind == LambdaJobKind.Entities) + { + WriteBurstCompileAttribute(writer); + WriteMonoPInvokeCallbackAttributeAttribute(writer); + + writer.WriteLine( + "public static void RunWithoutJobSystem(ref global::Unity.Entities.EntityQuery query, global::System.IntPtr jobPtr)"); + writer.WriteLine("{"); + writer.Indent++; + if (LambdaJobDescription.IsForDOTSRuntime) + writer.WriteLine("global::Unity.Runtime.TempMemoryScope.EnterScope();"); + writer.WriteLine("try"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine( + $"global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.RunWithoutJobsInternal(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeAsRef<{LambdaJobDescription.JobStructName}>(jobPtr), ref query);"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("finally"); + writer.WriteLine("{"); + writer.Indent++; + if (LambdaJobDescription.IsForDOTSRuntime) + writer.WriteLine("global::Unity.Runtime.TempMemoryScope.ExitScope();"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + } + else + { + WriteBurstCompileAttribute(writer); + WriteMonoPInvokeCallbackAttributeAttribute(writer); + writer.WriteLine("public static void RunWithoutJobSystem(global::System.IntPtr jobPtr)"); + writer.WriteLine("{"); + writer.Indent++; + if (LambdaJobDescription.IsForDOTSRuntime) + writer.WriteLine("global::Unity.Runtime.TempMemoryScope.EnterScope();"); + writer.WriteLine("try"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine( + $"global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeAsRef<{LambdaJobDescription.JobStructName}>(jobPtr).Execute();"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("finally"); + writer.WriteLine("{"); + writer.Indent++; + if (LambdaJobDescription.IsForDOTSRuntime) + writer.WriteLine("global::Unity.Runtime.TempMemoryScope.ExitScope();"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + } + } + + writer.Indent--; + writer.WriteLine("}"); + } + + void WriteExecuteMethod(IndentedTextWriter writer) + { + if (LambdaJobDescription.LambdaJobKind == LambdaJobKind.Job) + { + writer.WriteLine("public void Execute()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine(); + WritePerformLambda(writer); + writer.Indent--; + writer.WriteLine("}"); + } + + else if (LambdaJobDescription.WithStructuralChanges) + { + writer.WriteLine("public void RunWithStructuralChange(global::Unity.Entities.EntityQuery query)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"{TypeCreationHelpers.GeneratedLineTriviaToGeneratedSource}"); + writer.WriteLine($"var mask = query.GetEntityQueryMask();"); + writer.WriteLine( + $"global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeCreateGatherEntitiesResult(ref query, out var gatherEntitiesResult);"); + + foreach (var p in LambdaJobDescription.LambdaParameters) + { + var structuralChangesGetTypeIndex = p.StructuralChanges_GetTypeIndex(); + if (structuralChangesGetTypeIndex != null) + writer.WriteLine(structuralChangesGetTypeIndex); + } + + writer.WriteLine("try"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("int entityCount = gatherEntitiesResult.EntityCount;"); + writer.WriteLine("for (int entityIndex = 0; entityIndex != entityCount; entityIndex++)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine( + "var entity = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetEntityFromGatheredEntities(ref gatherEntitiesResult, entityIndex);"); + writer.WriteLine("if (mask.MatchesIgnoreFilter(entity))"); + writer.WriteLine("{"); + writer.Indent++; + foreach (var p in LambdaJobDescription.LambdaParameters) + { + var structuralChangesReadLambdaParam = p.StructuralChanges_ReadLambdaParam(); + if (structuralChangesReadLambdaParam != null) + writer.WriteLine(structuralChangesReadLambdaParam); + } + + foreach (var f in LambdaJobDescription.AdditionalFields) + writer.WriteLine($"{f.FieldName}.Update(__this);"); + + WritePerformLambda(writer); + + foreach (var p in LambdaJobDescription.LambdaParameters) + { + var structuralChangesWriteBackLambdaParam = p.StructuralChanges_WriteBackLambdaParam(); + if (structuralChangesWriteBackLambdaParam != null) + writer.WriteLine(structuralChangesWriteBackLambdaParam); + } + + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("finally"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine( + "global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeReleaseGatheredEntities(ref query, ref gatherEntitiesResult);"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + } + else + { + writer.WriteLine("[global::System.Runtime.CompilerServices.CompilerGenerated]"); + writer.WriteLine( + "public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchIndex, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"{TypeCreationHelpers.GeneratedLineTriviaToGeneratedSource}"); + foreach (var param in LambdaJobDescription.LambdaParameters) + { + var nativeArrayOrAccessor = param.GetNativeArrayOrAccessor(); + if (nativeArrayOrAccessor != null) + writer.WriteLine(nativeArrayOrAccessor); + } + + writer.WriteLine("int chunkEntityCount = chunk.Count;"); + if (LambdaJobDescription.NeedsEntityInQueryIndex) + writer.WriteLine("int matchingEntityCount = 0;"); + writer.WriteLine("if (!useEnabledMask)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("for(var entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex)"); + writer.WriteLine("{"); + writer.Indent++; + + if (LambdaJobDescription.NeedsEntityInQueryIndex) + writer.WriteLine( + "var entityInQueryIndex = __ChunkBaseEntityIndices[batchIndex] + matchingEntityCount++;"); + + WritePerformLambda(writer); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("else"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine( + "int edgeCount = global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong0 ^ (chunkEnabledMask.ULong0 << 1))" + + " + global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong1 ^ (chunkEnabledMask.ULong1 << 1)) - 1;"); + writer.WriteLine("bool useRanges = edgeCount <= 4;"); + writer.WriteLine("if (useRanges)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("int entityIndex = 0;"); + writer.WriteLine("int batchEndIndex = 0;"); + writer.WriteLine( + "while (global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeTryGetNextEnabledBitRange(chunkEnabledMask, batchEndIndex, out entityIndex, out batchEndIndex))"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("while (entityIndex < batchEndIndex)"); + writer.WriteLine("{"); + writer.Indent++; + if (LambdaJobDescription.NeedsEntityInQueryIndex) + writer.WriteLine( + "var entityInQueryIndex = __ChunkBaseEntityIndices[batchIndex] + matchingEntityCount++;"); + WritePerformLambda(writer); + writer.WriteLine("entityIndex++;"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("else"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("ulong mask64 = chunkEnabledMask.ULong0;"); + writer.WriteLine("int count = global::Unity.Mathematics.math.min(64, chunkEntityCount);"); + writer.WriteLine("for (var entityIndex = 0; entityIndex < count; ++entityIndex)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("if ((mask64 & 1) != 0)"); + writer.WriteLine("{"); + writer.Indent++; + if (LambdaJobDescription.NeedsEntityInQueryIndex) + writer.WriteLine( + "var entityInQueryIndex = __ChunkBaseEntityIndices[batchIndex] + matchingEntityCount++;"); + WritePerformLambda(writer); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("mask64 >>= 1;"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("mask64 = chunkEnabledMask.ULong1;"); + writer.WriteLine("for (var entityIndex = 64; entityIndex < chunkEntityCount; ++entityIndex)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("if ((mask64 & 1) != 0)"); + writer.WriteLine("{"); + writer.Indent++; + if (LambdaJobDescription.NeedsEntityInQueryIndex) + writer.WriteLine( + "var entityInQueryIndex = __ChunkBaseEntityIndices[batchIndex] + matchingEntityCount++;"); + WritePerformLambda(writer); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("mask64 >>= 1;"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + } + } + void WriteBurstCompileAttribute(IndentedTextWriter textWriter) + { + if (!LambdaJobDescription.Burst.IsEnabled) + return; + + var parameters = new List(capacity: 4); + if (LambdaJobDescription.Burst.Settings.BurstFloatMode != null) + parameters.Add($"FloatMode=global::Unity.Burst.FloatMode.{LambdaJobDescription.Burst.Settings.BurstFloatMode}"); + if (LambdaJobDescription.Burst.Settings.BurstFloatPrecision != null) + parameters.Add($"FloatPrecision=global::Unity.Burst.FloatPrecision.{LambdaJobDescription.Burst.Settings.BurstFloatPrecision}"); + if (LambdaJobDescription.Burst.Settings.SynchronousCompilation != null) + parameters.Add($"CompileSynchronously={LambdaJobDescription.Burst.Settings.SynchronousCompilation.ToString().ToLower()}"); + + if (parameters.Count == 0) + textWriter.WriteLine("[global::Unity.Burst.BurstCompile]"); + else + { + textWriter.WriteLine("[global::Unity.Burst.BurstCompile("); + for (var index = 0; index < parameters.Count; index++) + { + var p = parameters[index]; + textWriter.Write(p); + if (index < parameters.Count - 1) + textWriter.Write(", "); + } + textWriter.WriteLine(")]"); + } + } + + void WriteMonoPInvokeCallbackAttributeAttribute(IndentedTextWriter indentedTextWriter) + { + indentedTextWriter.WriteLine( + LambdaJobDescription.LambdaJobKind != LambdaJobKind.Entities + ? @"[global::AOT.MonoPInvokeCallback(typeof(global::Unity.Entities.Internal.InternalCompilerInterface.JobRunWithoutJobSystemDelegate))]" + : @"[global::AOT.MonoPInvokeCallback(typeof(global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate))]"); + } + + void WritePerformLambda(IndentedTextWriter writer) + { + foreach (var p in LambdaJobDescription.LambdaParameters) + { + var lambdaBodyParameterSetup = p.LambdaBodyParameterSetup(); + if (lambdaBodyParameterSetup != null) + writer.WriteLine(lambdaBodyParameterSetup); + } + writer.Write("OriginalLambdaBody("); + writer.Indent++; + + bool hasPrevParam = false; + if (LambdaJobDescription.WithStructuralChanges) + { + foreach (var p in LambdaJobDescription.LambdaParameters) + { + var methodParameter = p.StructuralChanges_LambdaBodyParameter(); + if (methodParameter != null) + { + if (hasPrevParam) + writer.Write(", "); + + writer.Write(methodParameter); + hasPrevParam = true; + } + } + } + else + { + foreach (var p in LambdaJobDescription.LambdaParameters) + { + var methodParameter = p.LambdaBodyParameter(); + if (methodParameter != null) + { + if (hasPrevParam) + writer.Write(", "); + + writer.Write(methodParameter); + hasPrevParam = true; + } + } + } + writer.Write(");"); + writer.WriteLine(); + writer.Indent--; + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaBodyRewriter.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaBodyRewriter.cs index 0355646..c4ffd80 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaBodyRewriter.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaBodyRewriter.cs @@ -1,181 +1,180 @@ +using System.Collections.Generic; +using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Collections.Generic; -using System.Linq; using Unity.Entities.SourceGen.Common; -using Unity.Entities.SourceGen.SystemGenerator; +using Unity.Entities.SourceGen.SystemGenerator.Common; using InvalidDescriptionException = Unity.Entities.SourceGen.Common.InvalidDescriptionException; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +// Describes the field in the LambdaJob struct that holds the accessor type for accessing data from entities +// (ComponentLookup, BufferLookup) +// These get added as members into the LambdaJob struct. + +// Rewrite the original lambda body with a few changes: +// 1. If we are accessing any methods/fields of our declaring type, make sure it is explicit +// ("this.EntityManager.DoThing()" instead of "EntityManager.DoThing()") +// 2. Replace all member access with "this" identifiers to "__this" (to access through stored field on job struct) +// 3. Patch all access through data access through entity methods (GetComponent, SetComponent, etc) +// 4. Adds trivia for line numbers from original syntax statements +// 5. Replace ForEach.Method() and ForEach.Property with either state.Method()/Property or just Method()/Property +// for ISystem or SystemBase, respectively +sealed class LambdaBodyRewriter { - // Describes the field in the LambdaJob struct that holds the accessor type for accessing data from entities - // (ComponentLookup, BufferLookup) - // These get added as members into the LambdaJob struct. - - // Rewrite the original lambda body with a few changes: - // 1. If we are accessing any methods/fields of our declaring type, make sure it is explicit - // ("this.EntityManager.DoThing()" instead of "EntityManager.DoThing()") - // 2. Replace all member access with "this" identifiers to "__this" (to access through stored field on job struct) - // 3. Patch all access through data access through entity methods (GetComponent, SetComponent, etc) - // 4. Adds trivia for line numbers from original syntax statements - // 5. Replace ForEach.Method() and ForEach.Property with either state.Method()/Property or just Method()/Property - // for ISystem or SystemBase, respectively - sealed class LambdaBodyRewriter - { - readonly LambdaJobDescription _lambdaJobDescription; + readonly LambdaJobDescription _lambdaJobDescription; // TODO: This was recently fixed (https://github.com/dotnet/roslyn-analyzers/issues/5804), remove pragmas after we update .net #pragma warning disable RS1024 - Dictionary DataLookupFields { get; } = - new Dictionary(SymbolEqualityComparer.Default); + Dictionary DataLookupFields { get; } = + new Dictionary(SymbolEqualityComparer.Default); #pragma warning restore RS1024 - public bool NeedsTimeData { get; private set; } + public bool NeedsTimeData { get; private set; } - public LambdaBodyRewriter(LambdaJobDescription lambdaJobDescription) - { - _lambdaJobDescription = lambdaJobDescription; - } + public LambdaBodyRewriter(LambdaJobDescription lambdaJobDescription) + { + _lambdaJobDescription = lambdaJobDescription; + } - internal (SyntaxNode rewrittenLambdaExpression, List additionalFields) Rewrite() + internal (SyntaxNode rewrittenLambdaExpression, List additionalFields) Rewrite() + { + // Find all locations where we are accessing a member on the declaring SystemBase + // and change them to access through "__this" instead. + // This also annotates the changed nodes so that we can find them later for patching (and get their original symbols). + var replacer = new LambdaBodySyntaxReplacer(_lambdaJobDescription); + var rewrittenLambdaBodyData = replacer.Rewrite(); + NeedsTimeData = replacer.NeedsTimeData; + var rewrittenLambdaExpression = rewrittenLambdaBodyData.rewrittenLambdaExpression; ; + + // Go through all changed nodes and check to see if they are a component access method that we need to patch (GetComponent/SetComponent/etc) + // Only need to do this if we are not doing structural changes (in which case we can't as structural changes will invalidate) + var replacedToOriginal = new Dictionary(rewrittenLambdaBodyData.thisAccessNodesNeedingReplacement.Count); + foreach (var originalNode in rewrittenLambdaBodyData.thisAccessNodesNeedingReplacement) { - // Find all locations where we are accessing a member on the declaring SystemBase - // and change them to access through "__this" instead. - // This also annotates the changed nodes so that we can find them later for patching (and get their original symbols). - var replacer = new LambdaBodySyntaxReplacer(_lambdaJobDescription); - var rewrittenLambdaBodyData = replacer.Rewrite(); - NeedsTimeData = replacer.NeedsTimeData; - var rewrittenLambdaExpression = rewrittenLambdaBodyData.rewrittenLambdaExpression; ; - - // Go through all changed nodes and check to see if they are a component access method that we need to patch (GetComponent/SetComponent/etc) - // Only need to do this if we are not doing structural changes (in which case we can't as structural changes will invalidate) - var replacedToOriginal = new Dictionary(rewrittenLambdaBodyData.thisAccessNodesNeedingReplacement.Count); - foreach (var originalNode in rewrittenLambdaBodyData.thisAccessNodesNeedingReplacement) - { - var originalInvocation = originalNode.AncestorOfKind(); + var originalInvocation = originalNode.AncestorOfKind(); - var currentNode = rewrittenLambdaExpression.GetCurrentNode(originalNode); - var currentNodeInvocationExpression = currentNode.AncestorOfKindOrDefault(); - if (currentNodeInvocationExpression == null) - continue; - replacedToOriginal[currentNodeInvocationExpression] = originalInvocation; - } - rewrittenLambdaExpression = rewrittenLambdaExpression.ReplaceNodes(replacedToOriginal.Keys, (node, replacedNode) - => CreateDataLookupFields_AndReplaceMemberAccessExpressionNodes(_lambdaJobDescription, replacedToOriginal[node], replacedNode, _lambdaJobDescription.SystemDescription.SemanticModel) ?? replacedNode); - - return (rewrittenLambdaExpression, DataLookupFields.Values.ToList()); + var currentNode = rewrittenLambdaExpression.GetCurrentNode(originalNode); + var currentNodeInvocationExpression = currentNode.AncestorOfKindOrDefault(); + if (currentNodeInvocationExpression == null) + continue; + replacedToOriginal[currentNodeInvocationExpression] = originalInvocation; } + rewrittenLambdaExpression = rewrittenLambdaExpression.ReplaceNodes(replacedToOriginal.Keys, (node, replacedNode) + => CreateDataLookupFields_AndReplaceMemberAccessExpressionNodes(_lambdaJobDescription, replacedToOriginal[node], replacedNode, _lambdaJobDescription.SystemDescription.SemanticModel) ?? replacedNode); + + return (rewrittenLambdaExpression, DataLookupFields.Values.ToList()); + } - SyntaxNode CreateDataLookupFields_AndReplaceMemberAccessExpressionNodes(LambdaJobDescription description, SyntaxNode originalNode, SyntaxNode replaced, SemanticModel model) + SyntaxNode CreateDataLookupFields_AndReplaceMemberAccessExpressionNodes(LambdaJobDescription description, SyntaxNode originalNode, SyntaxNode replaced, SemanticModel model) + { + if (originalNode is InvocationExpressionSyntax originalInvocationNode && replaced is InvocationExpressionSyntax replacedExpression + && model.GetSymbolInfo(originalInvocationNode.Expression) is var symbolInfo) { - if (originalNode is InvocationExpressionSyntax originalInvocationNode && replaced is InvocationExpressionSyntax replacedExpression - && model.GetSymbolInfo(originalInvocationNode.Expression) is var symbolInfo) - { - var methodSymbol = symbolInfo.Symbol as IMethodSymbol ?? symbolInfo.CandidateSymbols.FirstOrDefault() as IMethodSymbol; - if (methodSymbol == null) - return null; + var methodSymbol = symbolInfo.Symbol as IMethodSymbol ?? symbolInfo.CandidateSymbols.FirstOrDefault() as IMethodSymbol; + if (methodSymbol == null) + return null; - if (!(methodSymbol.ContainingType.Is("Unity.Entities.SystemBase") || methodSymbol.ContainingType.Is("Unity.Entities.SystemAPI"))) - return null; + if (!(methodSymbol.ContainingType.Is("Unity.Entities.SystemBase") || methodSymbol.ContainingType.Is("Unity.Entities.SystemAPI"))) + return null; - if (!LambdaJobsPatchableMethod.PatchableMethods.TryGetValue(methodSymbol.Name, out var patchableMethod)) - { - return null; - } + if (!LambdaJobsPatchableMethod.PatchableMethods.TryGetValue(methodSymbol.Name, out var patchableMethod)) + { + return null; + } - var readOnlyAccess = true; - switch (patchableMethod.AccessRights) - { - case LambdaJobsPatchableMethod.ComponentAccessRights.ReadOnly: - readOnlyAccess = true; - break; - case LambdaJobsPatchableMethod.ComponentAccessRights.ReadWrite: + var readOnlyAccess = true; + switch (patchableMethod.AccessRights) + { + case LambdaJobsPatchableMethod.ComponentAccessRights.ReadOnly: + readOnlyAccess = true; + break; + case LambdaJobsPatchableMethod.ComponentAccessRights.ReadWrite: + readOnlyAccess = false; + break; + + // Get read-access from method's param + case LambdaJobsPatchableMethod.ComponentAccessRights.GetFromFirstMethodParam: + if (originalInvocationNode.ArgumentList.Arguments.Count == 0) + { + // Default parameter value for GetComponentLookup/GetBufferLookup is false (aka `bool isReadOnly = false`) readOnlyAccess = false; - break; - - // Get read-access from method's param - case LambdaJobsPatchableMethod.ComponentAccessRights.GetFromFirstMethodParam: - if (originalInvocationNode.ArgumentList.Arguments.Count == 0) + } + else + { + var literalArgument = originalInvocationNode.ArgumentList.Arguments.FirstOrDefault()?.DescendantNodes().OfType().FirstOrDefault(); + if (literalArgument != null && description.SystemDescription.SemanticModel.GetConstantValue(literalArgument).Value is bool boolValue) { - // Default parameter value for GetComponentLookup/GetBufferLookup is false (aka `bool isReadOnly = false`) - readOnlyAccess = false; + readOnlyAccess = boolValue; } else { - var literalArgument = originalInvocationNode.ArgumentList.Arguments.FirstOrDefault()?.DescendantNodes().OfType().FirstOrDefault(); - if (literalArgument != null && description.SystemDescription.SemanticModel.GetConstantValue(literalArgument).Value is bool boolValue) - { - readOnlyAccess = boolValue; - } - else - { - LambdaJobsErrors.DC0059(description.SystemDescription, description.Location, methodSymbol.Name, description.LambdaJobKind); - description.Success = false; - throw new InvalidDescriptionException(); - } + LambdaJobsErrors.DC0059(description.SystemDescription, description.Location, methodSymbol.Name, description.LambdaJobKind); + description.Success = false; + throw new InvalidDescriptionException(); } + } - break; - } + break; + } - // If we have read/write access, we can only guaranteed safe access with sequential access (.Run or .Schedule) - if (!readOnlyAccess && description.Schedule.Mode == ScheduleMode.ScheduleParallel) + // If we have read/write access, we can only guaranteed safe access with sequential access (.Run or .Schedule) + if (!readOnlyAccess && description.Schedule.Mode == ScheduleMode.ScheduleParallel) + { + var patchedMethodTypeArgument = methodSymbol.TypeArguments.First(); + SystemGeneratorErrors.DC0063(description.SystemDescription, description.Location, methodSymbol.Name, patchedMethodTypeArgument.Name); + description.Success = false; + throw new InvalidDescriptionException(); + } + + // Make sure our ComponentLookupField doesn't give write access to a lambda parameter of the same type + // or there is a writable lambda parameter that gives access to this type (either could violate aliasing rules). + if (methodSymbol.TypeArguments.Length == 1) + { + foreach (var parameter in description.LambdaParameters) { var patchedMethodTypeArgument = methodSymbol.TypeArguments.First(); - SystemGeneratorErrors.DC0063(description.SystemDescription, description.Location, methodSymbol.Name, patchedMethodTypeArgument.Name); - description.Success = false; - throw new InvalidDescriptionException(); - } + if (parameter.TypeSymbol.ToFullName() != patchedMethodTypeArgument.ToFullName()) + continue; - // Make sure our ComponentLookupField doesn't give write access to a lambda parameter of the same type - // or there is a writable lambda parameter that gives access to this type (either could violate aliasing rules). - if (methodSymbol.TypeArguments.Length == 1) - { - foreach (var parameter in description.LambdaParameters) + if (!readOnlyAccess) { - var patchedMethodTypeArgument = methodSymbol.TypeArguments.First(); - if (parameter.TypeSymbol.ToFullName() != patchedMethodTypeArgument.ToFullName()) - continue; - - if (!readOnlyAccess) - { - LambdaJobsErrors.DC0046(description.SystemDescription, description.Location, methodSymbol.Name, parameter.TypeSymbol.Name, description.LambdaJobKind); - description.Success = false; - throw new InvalidDescriptionException(); - } + LambdaJobsErrors.DC0046(description.SystemDescription, description.Location, methodSymbol.Name, parameter.TypeSymbol.Name, description.LambdaJobKind); + description.Success = false; + throw new InvalidDescriptionException(); + } - if (!parameter.QueryTypeIsReadOnly()) - { - LambdaJobsErrors.DC0047(description.SystemDescription, description.Location, methodSymbol.Name, parameter.TypeSymbol.Name, description.LambdaJobKind); - description.Success = false; - throw new InvalidDescriptionException(); - } + if (!parameter.QueryTypeIsReadOnly()) + { + LambdaJobsErrors.DC0047(description.SystemDescription, description.Location, methodSymbol.Name, parameter.TypeSymbol.Name, description.LambdaJobKind); + description.Success = false; + throw new InvalidDescriptionException(); } } - - return patchableMethod.GeneratePatchedReplacementSyntax(methodSymbol, this, replacedExpression); } - return null; + return patchableMethod.GeneratePatchedReplacementSyntax(methodSymbol, this, replacedExpression); } - // Gets or created a field declaration for a type as needed. - // This will first check if a RW one is available, if that is the case we should use that. - // If not it will check to see if a RO one is available, use that and promote to RW if needed. - // Finally, if we don't have one at all, let's create one with the appropriate access rights - internal DataLookupFieldDescription GetOrCreateDataAccessField(ITypeSymbol type, bool asReadOnly, LambdaJobsPatchableMethod.AccessorDataType patchableMethodDataType) - { - if (DataLookupFields.TryGetValue(type, out var result)) - { - if (result.IsReadOnly && !asReadOnly) - DataLookupFields[type] = new DataLookupFieldDescription(false, type, patchableMethodDataType); + return null; + } - return DataLookupFields[type]; - } + // Gets or created a field declaration for a type as needed. + // This will first check if a RW one is available, if that is the case we should use that. + // If not it will check to see if a RO one is available, use that and promote to RW if needed. + // Finally, if we don't have one at all, let's create one with the appropriate access rights + internal DataLookupFieldDescription GetOrCreateDataAccessField(ITypeSymbol type, bool asReadOnly, LambdaJobsPatchableMethod.AccessorDataType patchableMethodDataType) + { + if (DataLookupFields.TryGetValue(type, out var result)) + { + if (result.IsReadOnly && !asReadOnly) + DataLookupFields[type] = new DataLookupFieldDescription(false, type, patchableMethodDataType); - DataLookupFields[type] = new DataLookupFieldDescription(asReadOnly, type, patchableMethodDataType); return DataLookupFields[type]; } + + DataLookupFields[type] = new DataLookupFieldDescription(asReadOnly, type, patchableMethodDataType); + return DataLookupFields[type]; } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaBodySyntaxReplacer.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaBodySyntaxReplacer.cs index 95cd500..0583123 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaBodySyntaxReplacer.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaBodySyntaxReplacer.cs @@ -2,489 +2,525 @@ // Licensed under the MIT License. See LICENSE in the project root for license information. using System; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; using Unity.Entities.SourceGen.Common; -using static Unity.Entities.SourceGen.LambdaJobs.LambdaParamDescription_EntityCommandBuffer; +using static Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.LambdaParamDescription_EntityCommandBuffer; + +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; -namespace Unity.Entities.SourceGen.LambdaJobs +// Find all cases where a member access could be used with explicit `this` +sealed class LambdaBodySyntaxReplacer : CSharpSyntaxWalker { - // Find all cases where a member access could be used with explicit `this` - sealed class LambdaBodySyntaxReplacer : CSharpSyntaxWalker - { - private readonly LambdaJobDescription _lambdaJobDescription; - private bool MustReplaceEcbNodesWithJobChunkParallelWriterNodes => - _lambdaJobDescription.EntityCommandBufferParameter is {Playback: {ScheduleMode: ScheduleMode.ScheduleParallel}}; + private readonly LambdaJobDescription _lambdaJobDescription; + private bool MustReplaceEcbNodesWithJobChunkParallelWriterNodes => + _lambdaJobDescription.EntityCommandBufferParameter is {Playback: {ScheduleMode: ScheduleMode.ScheduleParallel}}; - private bool MustReplaceEcbNodesWithJobChunkEcbNodes => - _lambdaJobDescription.EntityCommandBufferParameter != null && - _lambdaJobDescription.EntityCommandBufferParameter.Playback.ScheduleMode != ScheduleMode.ScheduleParallel; + private bool MustReplaceEcbNodesWithJobChunkEcbNodes => + _lambdaJobDescription.EntityCommandBufferParameter != null && + _lambdaJobDescription.EntityCommandBufferParameter.Playback.ScheduleMode != ScheduleMode.ScheduleParallel; - public bool NeedsTimeData { get; private set; } + public bool NeedsTimeData { get; private set; } - readonly List _lineStatementNodesNeedingReplacement = new List(); - readonly List _thisNodesNeedingReplacement = new List(); - readonly List _constantNodesNeedingReplacement = new List(); - readonly List _ecbNodesToReplaceWithJobChunkEcbNodes = new List(); - readonly List _SimpleMemberAccessExpressionsNeedingReplacement = new List(); + readonly List _lineStatementNodesNeedingReplacement = new List(); + readonly List _thisNodesNeedingReplacement = new List(); + readonly List _constantNodesNeedingReplacement = new List(); + readonly List _ecbNodesToReplaceWithJobChunkEcbNodes = new List(); + readonly List _SimpleMemberAccessExpressionsNeedingReplacement = new List(); - // These two lists are populated iff MustReplaceEntityCommandsMemberAccessNodesWithParallelWriterNodes is true. - // A node with index i in _entityCommandInvocationExpressionNodesToReplace is to be replaced by the node with the same index in _parallelWriterInvocationNodes. - // We use two lists instead of a dictionary because we need to access items in _parallelWriterInvocationNodes by index. - readonly List _ecbNodesToReplaceWithJobChunkParallelWriterNodes = new List(); - readonly List _parallelWriterInvocationNodes = new List(); + // These two lists are populated iff MustReplaceEntityCommandsMemberAccessNodesWithParallelWriterNodes is true. + // A node with index i in _entityCommandInvocationExpressionNodesToReplace is to be replaced by the node with the same index in _parallelWriterInvocationNodes. + // We use two lists instead of a dictionary because we need to access items in _parallelWriterInvocationNodes by index. + readonly List _ecbNodesToReplaceWithJobChunkParallelWriterNodes = new List(); + readonly List _parallelWriterInvocationNodes = new List(); - public (SyntaxNode rewrittenLambdaExpression, List thisAccessNodesNeedingReplacement) Rewrite() - { - Visit(_lambdaJobDescription.OriginalLambdaExpression); + public (SyntaxNode rewrittenLambdaExpression, List thisAccessNodesNeedingReplacement) Rewrite() + { + Visit(_lambdaJobDescription.OriginalLambdaExpression); - var allTrackedNodes = - _lineStatementNodesNeedingReplacement - .Concat(_thisNodesNeedingReplacement) - .Concat(_constantNodesNeedingReplacement) - .Concat(_ecbNodesToReplaceWithJobChunkEcbNodes) - .Concat(_ecbNodesToReplaceWithJobChunkParallelWriterNodes) - .Concat(_SimpleMemberAccessExpressionsNeedingReplacement); + var allTrackedNodes = + _lineStatementNodesNeedingReplacement + .Concat(_thisNodesNeedingReplacement) + .Concat(_constantNodesNeedingReplacement) + .Concat(_ecbNodesToReplaceWithJobChunkEcbNodes) + .Concat(_ecbNodesToReplaceWithJobChunkParallelWriterNodes) + .Concat(_SimpleMemberAccessExpressionsNeedingReplacement); - if (!allTrackedNodes.Any()) - { - return (_lambdaJobDescription.OriginalLambdaExpression, new List()); - } + if (!allTrackedNodes.Any()) + { + return (_lambdaJobDescription.OriginalLambdaExpression, new List()); + } - // Track nodes for later rewriting - var rewrittenBody = _lambdaJobDescription.OriginalLambdaExpression.TrackNodes(allTrackedNodes); + // Track nodes for later rewriting + var rewrittenBody = _lambdaJobDescription.OriginalLambdaExpression.TrackNodes(allTrackedNodes); - if (MustReplaceEcbNodesWithJobChunkEcbNodes) - { - var ecbParameterNodesToReplace = rewrittenBody.GetCurrentNodes(_ecbNodesToReplaceWithJobChunkEcbNodes); + if (MustReplaceEcbNodesWithJobChunkEcbNodes) + { + var ecbParameterNodesToReplace = rewrittenBody.GetCurrentNodes(_ecbNodesToReplaceWithJobChunkEcbNodes); - rewrittenBody = rewrittenBody.ReplaceNodes( - ecbParameterNodesToReplace, - (current, replacement) => SyntaxFactory.IdentifierName(GeneratedEcbFieldNameInJobChunkType)); - } - else if (MustReplaceEcbNodesWithJobChunkParallelWriterNodes) - { - var currentEntityCommandInvocationNodes = - rewrittenBody.GetCurrentNodes(_ecbNodesToReplaceWithJobChunkParallelWriterNodes).ToArray(); + rewrittenBody = rewrittenBody.ReplaceNodes( + ecbParameterNodesToReplace, + (current, replacement) => SyntaxFactory.IdentifierName(GeneratedEcbFieldNameInJobChunkType)); + } + else if (MustReplaceEcbNodesWithJobChunkParallelWriterNodes) + { + var currentEntityCommandInvocationNodes = + rewrittenBody.GetCurrentNodes(_ecbNodesToReplaceWithJobChunkParallelWriterNodes).ToArray(); - rewrittenBody = - rewrittenBody.ReplaceNodes( - currentEntityCommandInvocationNodes, - (current, _) => - { - var index = Array.IndexOf(currentEntityCommandInvocationNodes, current); - return _parallelWriterInvocationNodes[index]; - }); - } + rewrittenBody = + rewrittenBody.ReplaceNodes( + currentEntityCommandInvocationNodes, + (current, _) => + { + var index = Array.IndexOf(currentEntityCommandInvocationNodes, current); + return _parallelWriterInvocationNodes[index]; + }); + } - // Replace use of constants in the lambda with the actual constant value - var currentConstantNodesNeedingReplacement = rewrittenBody.GetCurrentNodes(_constantNodesNeedingReplacement); + // Replace use of constants in the lambda with the actual constant value + var currentConstantNodesNeedingReplacement = rewrittenBody.GetCurrentNodes(_constantNodesNeedingReplacement); - var oldToNewConstantNodesNeedingReplacement = - currentConstantNodesNeedingReplacement - .Zip( - _constantNodesNeedingReplacement, + var oldToNewConstantNodesNeedingReplacement = + currentConstantNodesNeedingReplacement + .Zip( + _constantNodesNeedingReplacement, (k, v) => new {key = k, value = v}) - .ToDictionary(x => x.key, x => x.value); + .ToDictionary(x => x.key, x => x.value); - rewrittenBody = - rewrittenBody.ReplaceNodes( - currentConstantNodesNeedingReplacement, - (originalNode, _) => - ReplaceConstantNodesWithConstantValue(_lambdaJobDescription.SystemDescription.SemanticModel, originalNode, oldToNewConstantNodesNeedingReplacement)); + rewrittenBody = + rewrittenBody.ReplaceNodes( + currentConstantNodesNeedingReplacement, + (originalNode, _) => + ReplaceConstantNodesWithConstantValue(_lambdaJobDescription.SystemDescription.SemanticModel, originalNode, oldToNewConstantNodesNeedingReplacement)); - // Replace MemberAccess that isn't invocationsyntax - if (_SimpleMemberAccessExpressionsNeedingReplacement.Count > 0) - { - rewrittenBody = rewrittenBody.ReplaceNodes( - rewrittenBody.GetCurrentNodes(_SimpleMemberAccessExpressionsNeedingReplacement), - (originalNode, _) => + // Replace MemberAccess that isn't invocationsyntax + if (_SimpleMemberAccessExpressionsNeedingReplacement.Count > 0) + { + rewrittenBody = rewrittenBody.ReplaceNodes( + rewrittenBody.GetCurrentNodes(_SimpleMemberAccessExpressionsNeedingReplacement), + (originalNode, _) => + { + switch (originalNode) { - switch (originalNode) - { - case IdentifierNameSyntax {Identifier: {ValueText: "Time"}}: - NeedsTimeData = true; - return SyntaxFactory.ParseExpression($"__Time"); - case MemberAccessExpressionSyntax {Name: {Identifier: {ValueText: "Time"}}}: - NeedsTimeData = true; - return SyntaxFactory.ParseExpression($"__Time"); - default: - return originalNode; - } - }); - } - - // Replace those locations to access through "__this" instead (since they need to access through a stored field on job struct). - // Also replace data access methods on SystemBase that need to be patched (GetComponent/SetComponent/etc) - var currentThisNodesNeedingReplacement = rewrittenBody.GetCurrentNodes(_thisNodesNeedingReplacement); - rewrittenBody = - rewrittenBody.ReplaceNodes(currentThisNodesNeedingReplacement, - (originalNode, _) => originalNode is MemberAccessExpressionSyntax ? originalNode - : SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,k_LocalThisFieldSyntax, (SimpleNameSyntax) originalNode)); + case IdentifierNameSyntax {Identifier: {ValueText: "Time"}}: + NeedsTimeData = true; + return SyntaxFactory.ParseExpression($"__Time"); + case MemberAccessExpressionSyntax {Name: {Identifier: {ValueText: "Time"}}}: + NeedsTimeData = true; + return SyntaxFactory.ParseExpression($"__Time"); + default: + return originalNode; + } + }); + } - // Also rewrite any remaining references to `this` to our local this - rewrittenBody = - rewrittenBody.ReplaceNodes(rewrittenBody.DescendantNodes().OfType(), + // Replace those locations to access through "__this" instead (since they need to access through a stored field on job struct). + // Also replace data access methods on SystemBase that need to be patched (GetComponent/SetComponent/etc) + var currentThisNodesNeedingReplacement = rewrittenBody.GetCurrentNodes(_thisNodesNeedingReplacement); + rewrittenBody = + rewrittenBody.ReplaceNodes(currentThisNodesNeedingReplacement, + (originalNode, _) => originalNode is MemberAccessExpressionSyntax ? originalNode + : SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,k_LocalThisFieldSyntax, (SimpleNameSyntax) originalNode)); + + // Also rewrite any remaining references to `this` to our local this + rewrittenBody = + rewrittenBody.ReplaceNodes(rewrittenBody.DescendantNodes().OfType(), (old, _ ) => k_LocalThisFieldSyntax); - // Replace line statements with one's with line directive trivia - foreach (var originalNode in _lineStatementNodesNeedingReplacement) + // Replace line statements with one's with line directive trivia + foreach (var originalNode in _lineStatementNodesNeedingReplacement) + { + var currentNode = rewrittenBody.GetCurrentNode(originalNode); + rewrittenBody = rewrittenBody.ReplaceNode(currentNode, WithLineTrivia(currentNode, originalNode.SyntaxTree.FilePath, originalNode.GetLineNumber())); + } + + return (rewrittenBody, _thisNodesNeedingReplacement); + } + + static SyntaxNode WithLineTrivia(SyntaxNode node, string originalFilePath, int originalLineNumber, int offsetLineNumber = 1) + { + if (string.IsNullOrEmpty(originalFilePath)) + return node; + + var lineTrivia = SyntaxFactory.Comment($"#line {originalLineNumber + offsetLineNumber} \"{originalFilePath}\""); + return node.WithLeadingTrivia(lineTrivia, SyntaxFactory.CarriageReturnLineFeed); + } + + static readonly ExpressionSyntax k_LocalThisFieldSyntax = SyntaxFactory.IdentifierName("__this"); + static SyntaxNode ReplaceConstantNodesWithConstantValue(SemanticModel model, SyntaxNode node, Dictionary oldToNewConstantNodesNeedingReplacement) + { + var originalNode = oldToNewConstantNodesNeedingReplacement[node]; + var symbolInfo = model.GetSymbolInfo(originalNode); + + if (symbolInfo.Symbol is ILocalSymbol {HasConstantValue: true} localSymbol) + { + try { - var currentNode = rewrittenBody.GetCurrentNode(originalNode); - rewrittenBody = rewrittenBody.ReplaceNode(currentNode, currentNode.WithLineTrivia(originalNode.SyntaxTree.FilePath, originalNode.GetLineNumber())); + // Need to special case float or else it is output as a double value + if (localSymbol.ConstantValue is float floatConstant) + return SyntaxFactory.ParseExpression($"{floatConstant.ToString(CultureInfo.InvariantCulture)}f"); + return + SyntaxFactory.ParseExpression(GetTypedConstantKind(localSymbol.Type) == TypedConstantKind.Enum + ? $"({localSymbol.Type.ToFullName()}){localSymbol.ConstantValue}" + : localSymbol.ConstantValue.ToString()); + } + catch (Exception) + { + throw new InvalidOperationException($"Could not parse constant literal expression for symbol in node {originalNode}"); } - - return (rewrittenBody, _thisNodesNeedingReplacement); } + throw new InvalidOperationException($"Unable to find symbol info with constant value for symbol in node {originalNode}"); - static readonly ExpressionSyntax k_LocalThisFieldSyntax = SyntaxFactory.IdentifierName("__this"); - static SyntaxNode ReplaceConstantNodesWithConstantValue(SemanticModel model, SyntaxNode node, Dictionary oldToNewConstantNodesNeedingReplacement) + static TypedConstantKind GetTypedConstantKind(ITypeSymbol type) { - var originalNode = oldToNewConstantNodesNeedingReplacement[node]; - var symbolInfo = model.GetSymbolInfo(originalNode); - - if (symbolInfo.Symbol is ILocalSymbol {HasConstantValue: true} localSymbol) + return type.SpecialType switch { - try - { - // Need to special case float or else it is output as a double value - if (localSymbol.ConstantValue is float floatConstant) - return SyntaxFactory.ParseExpression($"{floatConstant.ToString(CultureInfo.InvariantCulture)}f"); - else if (localSymbol.Type.GetTypedConstantKind() == TypedConstantKind.Enum) - return SyntaxFactory.ParseExpression($"({localSymbol.Type.ToFullName()}){localSymbol.ConstantValue}"); - else - return SyntaxFactory.ParseExpression(localSymbol.ConstantValue.ToString()); - } - catch (Exception) + SpecialType.System_Boolean => TypedConstantKind.Primitive, + SpecialType.System_SByte => TypedConstantKind.Primitive, + SpecialType.System_Int16 => TypedConstantKind.Primitive, + SpecialType.System_Int32 => TypedConstantKind.Primitive, + SpecialType.System_Int64 => TypedConstantKind.Primitive, + SpecialType.System_Byte => TypedConstantKind.Primitive, + SpecialType.System_UInt16 => TypedConstantKind.Primitive, + SpecialType.System_UInt32 => TypedConstantKind.Primitive, + SpecialType.System_UInt64 => TypedConstantKind.Primitive, + SpecialType.System_Single => TypedConstantKind.Primitive, + SpecialType.System_Double => TypedConstantKind.Primitive, + SpecialType.System_Char => TypedConstantKind.Primitive, + SpecialType.System_String => TypedConstantKind.Primitive, + SpecialType.System_Object => TypedConstantKind.Primitive, + _ => type.TypeKind switch { - throw new InvalidOperationException($"Could not parse constant literal expression for symbol in node {originalNode}"); + TypeKind.Array => TypedConstantKind.Array, + TypeKind.Enum => TypedConstantKind.Enum, + TypeKind.Error => TypedConstantKind.Error, + _ => TypedConstantKind.Type } - } - throw new InvalidOperationException($"Unable to find symbol info with constant value for symbol in node {originalNode}"); + }; } + } - public LambdaBodySyntaxReplacer(LambdaJobDescription lambdaJobDescription) + public LambdaBodySyntaxReplacer(LambdaJobDescription lambdaJobDescription) + { + _lambdaJobDescription = lambdaJobDescription; + } + + public override void Visit(SyntaxNode node) + { + switch (node) { - _lambdaJobDescription = lambdaJobDescription; + case StatementSyntax statementSyntax when !(node is BlockSyntax): + HandleStatement(statementSyntax); + break; + case SimpleNameSyntax simpleNameSyntax: + HandleSimpleName(simpleNameSyntax); + break; + case MemberAccessExpressionSyntax {Expression: IdentifierNameSyntax identifierNameSyntax}: + HandleIdentifierName(identifierNameSyntax); + break; + case InvocationExpressionSyntax invocationExpressionSyntax: + HandleInvocationExpressionSyntax(invocationExpressionSyntax); + break; } - public override void Visit(SyntaxNode node) + base.Visit(node); + } + + void HandleInvocationExpressionSyntax(InvocationExpressionSyntax originalNode) + { + if (_lambdaJobDescription.SystemDescription.SemanticModel.GetOperation(originalNode) is IInvocationOperation invocationOperation) { - switch (node) + var isEcbMethod = invocationOperation.TargetMethod.ContainingType.Is("global::Unity.Entities.EntityCommandBuffer") + || invocationOperation.TargetMethod.ContainingType.Is("Unity.Entities.EntityCommandBufferManagedComponentExtensions"); + + if (!isEcbMethod) { - case StatementSyntax statementSyntax when !(node is BlockSyntax): - HandleStatement(statementSyntax); - break; - case SimpleNameSyntax simpleNameSyntax: - HandleSimpleName(simpleNameSyntax); - break; - case MemberAccessExpressionSyntax {Expression: IdentifierNameSyntax identifierNameSyntax}: - HandleIdentifierName(identifierNameSyntax); - break; - case InvocationExpressionSyntax invocationExpressionSyntax: - HandleInvocationExpressionSyntax(invocationExpressionSyntax); - break; + return; } - base.Visit(node); - } - - void HandleInvocationExpressionSyntax(InvocationExpressionSyntax originalNode) - { - if (_lambdaJobDescription.SystemDescription.SemanticModel.GetOperation(originalNode) is IInvocationOperation invocationOperation) + if (originalNode.Expression is MemberAccessExpressionSyntax {Expression: IdentifierNameSyntax identifierNameSyntax} + && _lambdaJobDescription.SystemDescription.SemanticModel.GetOperation(identifierNameSyntax) is IParameterReferenceOperation) { - var isEcbMethod = invocationOperation.TargetMethod.ContainingType.Is("global::Unity.Entities.EntityCommandBuffer") - || invocationOperation.TargetMethod.ContainingType.Is("Unity.Entities.EntityCommandBufferManagedComponentExtensions"); + bool isSupportedInEntitiesForEach = invocationOperation.TargetMethod.GetAttributes().Any(a => a.AttributeClass.Is("Unity.Entities.SupportedInEntitiesForEach")); - if (!isEcbMethod) + if (!isSupportedInEntitiesForEach) { - return; + LambdaJobsErrors.DC0079( + _lambdaJobDescription.SystemDescription, + invocationOperation.TargetMethod.ToDisplayString(), + originalNode.GetLocation()); + + throw new InvalidDescriptionException(); } - if (originalNode.Expression is MemberAccessExpressionSyntax {Expression: IdentifierNameSyntax identifierNameSyntax} - && _lambdaJobDescription.SystemDescription.SemanticModel.GetOperation(identifierNameSyntax) is IParameterReferenceOperation) + if (_lambdaJobDescription.Schedule.Mode != ScheduleMode.Run && !CanRunOutsideOfMainThread(invocationOperation)) { - bool isSupportedInEntitiesForEach = invocationOperation.TargetMethod.GetAttributes().Any(a => a.AttributeClass.Is("Unity.Entities.SupportedInEntitiesForEach")); - - if (!isSupportedInEntitiesForEach) - { - LambdaJobsErrors.DC0079( - _lambdaJobDescription.SystemDescription, - invocationOperation.TargetMethod.ToDisplayString(), - originalNode.GetLocation()); - - throw new InvalidDescriptionException(); - } - - if (_lambdaJobDescription.Schedule.Mode != ScheduleMode.Run && !CanRunOutsideOfMainThread(invocationOperation)) - { - LambdaJobsErrors.DC0080( - _lambdaJobDescription.SystemDescription, - invocationOperation.TargetMethod.ToDisplayString(), - originalNode.GetLocation()); + LambdaJobsErrors.DC0080( + _lambdaJobDescription.SystemDescription, + invocationOperation.TargetMethod.ToDisplayString(), + originalNode.GetLocation()); - throw new InvalidDescriptionException(); - } + throw new InvalidDescriptionException(); + } - if (MustReplaceEcbNodesWithJobChunkParallelWriterNodes) - { - _ecbNodesToReplaceWithJobChunkParallelWriterNodes.Add(originalNode); + if (MustReplaceEcbNodesWithJobChunkParallelWriterNodes) + { + _ecbNodesToReplaceWithJobChunkParallelWriterNodes.Add(originalNode); - var replacementNode = ParallelEcbInvocationsReplacer.CreateReplacement(originalNode, invocationOperation); - _parallelWriterInvocationNodes.Add(replacementNode); - } + var replacementNode = ParallelEcbInvocationsReplacer.CreateReplacement(originalNode, invocationOperation); + _parallelWriterInvocationNodes.Add(replacementNode); } } } + } - bool CanRunOutsideOfMainThread(IInvocationOperation operation) - { - if (operation.TargetMethod.ContainingType.Is("Unity.Entities.EntityCommandBufferManagedComponentExtensions")) - return false; + bool CanRunOutsideOfMainThread(IInvocationOperation operation) + { + if (operation.TargetMethod.ContainingType.Is("Unity.Entities.EntityCommandBufferManagedComponentExtensions")) + return false; - return !operation.TargetMethod.Parameters.Any(p => p.Type.Is("Unity.Entities.EntityQuery")); - } + return !operation.TargetMethod.Parameters.Any(p => p.Type.Is("Unity.Entities.EntityQuery")); + } - void HandleStatement(StatementSyntax statementSyntax) - { - _lineStatementNodesNeedingReplacement.Add(statementSyntax); - } + void HandleStatement(StatementSyntax statementSyntax) + { + _lineStatementNodesNeedingReplacement.Add(statementSyntax); + } - void HandleSimpleName(SimpleNameSyntax node) + void HandleSimpleName(SimpleNameSyntax node) + { + switch (node?.Parent?.Kind() ?? SyntaxKind.None) { - switch (node?.Parent?.Kind() ?? SyntaxKind.None) - { - case SyntaxKind.SimpleMemberAccessExpression: - // this is handled separately - return; + case SyntaxKind.SimpleMemberAccessExpression: + // this is handled separately + return; - case SyntaxKind.MemberBindingExpression: - case SyntaxKind.NameColon: - case SyntaxKind.PointerMemberAccessExpression: - // this doesn't need to be handled - return; + case SyntaxKind.MemberBindingExpression: + case SyntaxKind.NameColon: + case SyntaxKind.PointerMemberAccessExpression: + // this doesn't need to be handled + return; + + case SyntaxKind.QualifiedCref: + case SyntaxKind.NameMemberCref: + // documentation comments don't use 'this.' + return; - case SyntaxKind.QualifiedCref: - case SyntaxKind.NameMemberCref: - // documentation comments don't use 'this.' + case SyntaxKind.SimpleAssignmentExpression: + if (((AssignmentExpressionSyntax)node.Parent).Left == node + && (node.Parent.Parent?.IsKind(SyntaxKind.ObjectInitializerExpression) ?? true)) + { + /* Handle 'X' in: + * new TypeName() { X = 3 } + */ return; + } - case SyntaxKind.SimpleAssignmentExpression: - if (((AssignmentExpressionSyntax)node.Parent).Left == node - && (node.Parent.Parent?.IsKind(SyntaxKind.ObjectInitializerExpression) ?? true)) - { - /* Handle 'X' in: - * new TypeName() { X = 3 } - */ - return; - } + break; + case SyntaxKind.NameEquals: + if (((NameEqualsSyntax)node.Parent).Name != node) + { break; + } - case SyntaxKind.NameEquals: - if (((NameEqualsSyntax)node.Parent).Name != node) - { - break; - } + switch (node?.Parent?.Parent?.Kind()) + { + case SyntaxKind.AttributeArgument: + case SyntaxKind.AnonymousObjectMemberDeclarator: + return; + } - switch (node?.Parent?.Parent?.Kind()) - { - case SyntaxKind.AttributeArgument: - case SyntaxKind.AnonymousObjectMemberDeclarator: - return; - } + break; - break; + case SyntaxKind.Argument when IsPartOfConstructorInitializer(node): + // constructor invocations cannot contain this. + return; + } - case SyntaxKind.Argument when IsPartOfConstructorInitializer(node): - // constructor invocations cannot contain this. - return; - } + HandleIdentifierName(node); + } - HandleIdentifierName(node); - } + void HandleIdentifierName(SimpleNameSyntax nameSyntax) + { + if (nameSyntax == null) + return; + + var symbolInfo = _lambdaJobDescription.SystemDescription.SemanticModel.GetSymbolInfo(nameSyntax); - void HandleIdentifierName(SimpleNameSyntax nameSyntax) + // Support `using static SystemAPI` + if (symbolInfo.Symbol != null && symbolInfo.Symbol.ContainingType.Is("Unity.Entities.SystemAPI")) { - if (nameSyntax == null) - return; + if (symbolInfo.Symbol.Kind == SymbolKind.Method) + _thisNodesNeedingReplacement.Add(nameSyntax); + else + _SimpleMemberAccessExpressionsNeedingReplacement.Add(nameSyntax); - var symbolInfo = _lambdaJobDescription.SystemDescription.SemanticModel.GetSymbolInfo(nameSyntax); + return; + } - // Support `using static SystemAPI` - if (symbolInfo.Symbol != null && symbolInfo.Symbol.ContainingType.Is("Unity.Entities.SystemAPI")) + // Support `SystemAPI.***` + if (symbolInfo.Symbol is {IsStatic: true} && symbolInfo.Symbol.ToDisplayString() == "Unity.Entities.SystemAPI") + { + if (nameSyntax.Parent != null) { - if (symbolInfo.Symbol.Kind == SymbolKind.Method) - _thisNodesNeedingReplacement.Add(nameSyntax); + var parentSymbolInfo = _lambdaJobDescription.SystemDescription.SemanticModel.GetSymbolInfo(nameSyntax.Parent); + if (parentSymbolInfo.Symbol is {Kind: SymbolKind.Method}) + _thisNodesNeedingReplacement.Add(nameSyntax.Parent); else - _SimpleMemberAccessExpressionsNeedingReplacement.Add(nameSyntax); - - return; + _SimpleMemberAccessExpressionsNeedingReplacement.Add(nameSyntax.Parent); } - // Support `SystemAPI.***` - if (symbolInfo.Symbol is {IsStatic: true} && symbolInfo.Symbol.ToDisplayString() == "Unity.Entities.SystemAPI") - { - if (nameSyntax.Parent != null) - { - var parentSymbolInfo = _lambdaJobDescription.SystemDescription.SemanticModel.GetSymbolInfo(nameSyntax.Parent); - if (parentSymbolInfo.Symbol is {Kind: SymbolKind.Method}) - _thisNodesNeedingReplacement.Add(nameSyntax.Parent); - else - _SimpleMemberAccessExpressionsNeedingReplacement.Add(nameSyntax.Parent); - } + return; + } - return; - } + if (!HasThis(nameSyntax)) + return; - if (!HasThis(nameSyntax)) - return; + if (symbolInfo.Symbol is ILocalSymbol {IsConst: true, HasConstantValue: true}) + { + _constantNodesNeedingReplacement.Add(nameSyntax); + return; + } - if (symbolInfo.Symbol is ILocalSymbol {IsConst: true, HasConstantValue: true}) - { - _constantNodesNeedingReplacement.Add(nameSyntax); + if (symbolInfo.Symbol is IParameterSymbol parameterSymbol + && parameterSymbol.Type.Is("Unity.Entities.EntityCommandBuffer") + && MustReplaceEcbNodesWithJobChunkEcbNodes) + { + _ecbNodesToReplaceWithJobChunkEcbNodes.Add(nameSyntax); + return; + } + + ImmutableArray symbolsToAnalyze; + if (symbolInfo.Symbol != null) + symbolsToAnalyze = ImmutableArray.Create(symbolInfo.Symbol); + // Bug in roslyn when resolving multiple constraint generic method symbols (causes OverloadResolutionFailure): https://github.com/dotnet/roslyn/issues/61504 + else if (symbolInfo.CandidateReason == CandidateReason.MemberGroup || symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure) + // analyze the complete set of candidates, and use 'this.' if it applies to all + symbolsToAnalyze = symbolInfo.CandidateSymbols; + else + return; + + foreach (var symbol in symbolsToAnalyze) + { + if (symbol is ITypeSymbol) return; - } - if (symbolInfo.Symbol is IParameterSymbol parameterSymbol - && parameterSymbol.Type.Is("Unity.Entities.EntityCommandBuffer") - && MustReplaceEcbNodesWithJobChunkEcbNodes) - { - _ecbNodesToReplaceWithJobChunkEcbNodes.Add(nameSyntax); + if (symbol.IsStatic) return; - } - ImmutableArray symbolsToAnalyze; - if (symbolInfo.Symbol != null) - symbolsToAnalyze = ImmutableArray.Create(symbolInfo.Symbol); - // Bug in roslyn when resolving multiple constraint generic method symbols (causes OverloadResolutionFailure): https://github.com/dotnet/roslyn/issues/61504 - else if (symbolInfo.CandidateReason == CandidateReason.MemberGroup || symbolInfo.CandidateReason == CandidateReason.OverloadResolutionFailure) - // analyze the complete set of candidates, and use 'this.' if it applies to all - symbolsToAnalyze = symbolInfo.CandidateSymbols; - else + if (!(symbol.ContainingSymbol is ITypeSymbol)) + // covers local variables, parameters, etc. return; - foreach (var symbol in symbolsToAnalyze) + if (symbol is IMethodSymbol methodSymbol) { - if (symbol is ITypeSymbol) - return; - - if (symbol.IsStatic) - return; - - if (!(symbol.ContainingSymbol is ITypeSymbol)) - // covers local variables, parameters, etc. - return; - - if (symbol is IMethodSymbol methodSymbol) + switch (methodSymbol.MethodKind) { - switch (methodSymbol.MethodKind) - { - case MethodKind.Constructor: - case MethodKind.LocalFunction: - case MethodKind.LambdaMethod: - return; - } + case MethodKind.Constructor: + case MethodKind.LocalFunction: + case MethodKind.LambdaMethod: + return; } + } + + // This is a workaround for: + // - https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1501 + // - https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2093 + // and can be removed when the underlying bug in roslyn is resolved + if (nameSyntax.Parent is MemberAccessExpressionSyntax) + { + var memberAccessSymbol = _lambdaJobDescription.SystemDescription.SemanticModel.GetSymbolInfo(nameSyntax.Parent).Symbol; - // This is a workaround for: - // - https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/1501 - // - https://github.com/DotNetAnalyzers/StyleCopAnalyzers/issues/2093 - // and can be removed when the underlying bug in roslyn is resolved - if (nameSyntax.Parent is MemberAccessExpressionSyntax) + switch (memberAccessSymbol?.Kind) { - var memberAccessSymbol = _lambdaJobDescription.SystemDescription.SemanticModel.GetSymbolInfo(nameSyntax.Parent).Symbol; + case null: + break; - switch (memberAccessSymbol?.Kind) - { - case null: - break; - - case SymbolKind.Field: - case SymbolKind.Method: - case SymbolKind.Property: - if (memberAccessSymbol.IsStatic && (memberAccessSymbol.ContainingType.Name == symbol.Name)) - { - return; - } - - break; - } - } + case SymbolKind.Field: + case SymbolKind.Method: + case SymbolKind.Property: + if (memberAccessSymbol.IsStatic && (memberAccessSymbol.ContainingType.Name == symbol.Name)) + { + return; + } - // End of workaround + break; + } } - _thisNodesNeedingReplacement.Add(nameSyntax); + // End of workaround } - static bool HasThis(SyntaxNode node) + _thisNodesNeedingReplacement.Add(nameSyntax); + } + + static bool HasThis(SyntaxNode node) + { + for (; node != null; node = node.Parent) { - for (; node != null; node = node.Parent) + switch (node.Kind()) { - switch (node.Kind()) - { - case SyntaxKind.ClassDeclaration: - case SyntaxKind.InterfaceDeclaration: - case SyntaxKind.StructDeclaration: - case SyntaxKind.DelegateDeclaration: - case SyntaxKind.EnumDeclaration: - case SyntaxKind.NamespaceDeclaration: - return false; - - case SyntaxKind.FieldDeclaration: - case SyntaxKind.EventFieldDeclaration: - return false; - - case SyntaxKind.EventDeclaration: - case SyntaxKind.IndexerDeclaration: - var basePropertySyntax = (BasePropertyDeclarationSyntax)node; - return !basePropertySyntax.Modifiers.Any(SyntaxKind.StaticKeyword); - - case SyntaxKind.PropertyDeclaration: - var propertySyntax = (PropertyDeclarationSyntax)node; - return !propertySyntax.Modifiers.Any(SyntaxKind.StaticKeyword) && propertySyntax.Initializer == null; - - case SyntaxKind.MultiLineDocumentationCommentTrivia: - case SyntaxKind.SingleLineDocumentationCommentTrivia: - return false; - - case SyntaxKind.ConstructorDeclaration: - case SyntaxKind.DestructorDeclaration: - case SyntaxKind.MethodDeclaration: - var baseMethodSyntax = (BaseMethodDeclarationSyntax)node; - return !baseMethodSyntax.Modifiers.Any(SyntaxKind.StaticKeyword); - - case SyntaxKind.Attribute: - return false; - - default: - continue; - } + case SyntaxKind.ClassDeclaration: + case SyntaxKind.InterfaceDeclaration: + case SyntaxKind.StructDeclaration: + case SyntaxKind.DelegateDeclaration: + case SyntaxKind.EnumDeclaration: + case SyntaxKind.NamespaceDeclaration: + return false; + + case SyntaxKind.FieldDeclaration: + case SyntaxKind.EventFieldDeclaration: + return false; + + case SyntaxKind.EventDeclaration: + case SyntaxKind.IndexerDeclaration: + var basePropertySyntax = (BasePropertyDeclarationSyntax)node; + return !basePropertySyntax.Modifiers.Any(SyntaxKind.StaticKeyword); + + case SyntaxKind.PropertyDeclaration: + var propertySyntax = (PropertyDeclarationSyntax)node; + return !propertySyntax.Modifiers.Any(SyntaxKind.StaticKeyword) && propertySyntax.Initializer == null; + + case SyntaxKind.MultiLineDocumentationCommentTrivia: + case SyntaxKind.SingleLineDocumentationCommentTrivia: + return false; + + case SyntaxKind.ConstructorDeclaration: + case SyntaxKind.DestructorDeclaration: + case SyntaxKind.MethodDeclaration: + var baseMethodSyntax = (BaseMethodDeclarationSyntax)node; + return !baseMethodSyntax.Modifiers.Any(SyntaxKind.StaticKeyword); + + case SyntaxKind.Attribute: + return false; + + default: + continue; } - - return false; } - static bool IsPartOfConstructorInitializer(SyntaxNode node) + return false; + } + + static bool IsPartOfConstructorInitializer(SyntaxNode node) + { + for (; node != null; node = node.Parent) { - for (; node != null; node = node.Parent) + switch (node.Kind()) { - switch (node.Kind()) - { - case SyntaxKind.ThisConstructorInitializer: - case SyntaxKind.BaseConstructorInitializer: - return true; - } + case SyntaxKind.ThisConstructorInitializer: + case SyntaxKind.BaseConstructorInitializer: + return true; } - - return false; } + + return false; } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaCapturedVariableDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaCapturedVariableDescription.cs index e60bf16..394d66d 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaCapturedVariableDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaCapturedVariableDescription.cs @@ -6,122 +6,121 @@ using Unity.Entities.SourceGen.Common; using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +public class LambdaCapturedVariableDescription { - public class LambdaCapturedVariableDescription + public ISymbol Symbol { get; } + public bool IsThis => ExplicitThis || Symbol.Name == "this"; + public string VariableFieldName => $"{(IsThis ? "__this" : Symbol.Name)}"; + public string OriginalVariableName => $"{(IsThis ? "this" : Symbol.Name)}"; + public List Attributes { get; } + public bool IsWritable { get; } + + bool ExplicitThis { get; } + + public ITypeSymbol Type => Symbol switch { - public ISymbol Symbol { get; } - public bool IsThis => ExplicitThis || Symbol.Name == "this"; - public string VariableFieldName => $"{(IsThis ? "__this" : Symbol.Name)}"; - public string OriginalVariableName => $"{(IsThis ? "this" : Symbol.Name)}"; - public List Attributes { get; } - public bool IsWritable { get; } + ILocalSymbol localSymbol => localSymbol.Type, + IParameterSymbol parameterSymbol => parameterSymbol.Type, + ITypeSymbol typeSymbol => typeSymbol, + _ => throw new InvalidOperationException($"Cannot discover type for symbol {Symbol}") + }; - bool ExplicitThis { get; } + public bool IsNativeContainer => Type.GetAttributes().Any(attribute => attribute.AttributeClass.ToFullName() == "Unity.Collections.LowLevel.Unsafe.NativeContainerAttribute"); - public ITypeSymbol Type => Symbol switch - { - ILocalSymbol localSymbol => localSymbol.Type, - IParameterSymbol parameterSymbol => parameterSymbol.Type, - ITypeSymbol typeSymbol => typeSymbol, - _ => throw new InvalidOperationException($"Cannot discover type for symbol {Symbol}") - }; + public LambdaCapturedVariableDescription(ISymbol symbol, bool explicitThis = false) + { + Symbol = symbol; + Attributes = new List(); + ExplicitThis = explicitThis; + + // This is not fantastic, but I haven't found a better way to tell if this was declared using a using statement, + // in which case, we cannot write back to this variable. + // Searching for a better way: https://stackoverflow.com/questions/64467518/roslyn-detect-when-a-local-variable-has-been-declared-with-using + IsWritable = true; + var declaringSyntax = symbol.OriginalDefinition.DeclaringSyntaxReferences.FirstOrDefault(); + if (declaringSyntax?.GetSyntax().Parent is VariableDeclarationSyntax variableDeclarationSyntax && + variableDeclarationSyntax?.Parent is UsingStatementSyntax) + IsWritable = false; + } - public bool IsNativeContainer => Type.GetAttributes().Any(attribute => attribute.AttributeClass.ToFullName() == "Unity.Collections.LowLevel.Unsafe.NativeContainerAttribute"); + public delegate bool CheckAttributeApplicable(SystemDescription systemDescription, LambdaCapturedVariableDescription capturedVariableDescription, string methodName); - public LambdaCapturedVariableDescription(ISymbol symbol, bool explicitThis = false) + public readonly struct AttributeDescription + { + public AttributeDescription(string methodName, string attributeName, CheckAttributeApplicable check = null) { - Symbol = symbol; - Attributes = new List(); - ExplicitThis = explicitThis; - - // This is not fantastic, but I haven't found a better way to tell if this was declared using a using statement, - // in which case, we cannot write back to this variable. - // Searching for a better way: https://stackoverflow.com/questions/64467518/roslyn-detect-when-a-local-variable-has-been-declared-with-using - IsWritable = true; - var declaringSyntax = symbol.OriginalDefinition.DeclaringSyntaxReferences.FirstOrDefault(); - if (declaringSyntax?.GetSyntax().Parent is VariableDeclarationSyntax variableDeclarationSyntax && - variableDeclarationSyntax?.Parent is UsingStatementSyntax) - IsWritable = false; + MethodName = methodName; + AttributeName = attributeName; + m_CheckAttributeApplicable = check; } - public delegate bool CheckAttributeApplicable(SystemDescription systemDescription, LambdaCapturedVariableDescription capturedVariableDescription, string methodName); - - public readonly struct AttributeDescription - { - public AttributeDescription(string methodName, string attributeName, CheckAttributeApplicable check = null) - { - MethodName = methodName; - AttributeName = attributeName; - m_CheckAttributeApplicable = check; - } + public readonly string MethodName; + public readonly string AttributeName; + readonly CheckAttributeApplicable m_CheckAttributeApplicable; + public bool IsApplicableToCaptured(SystemDescription systemDescription, LambdaCapturedVariableDescription capturedVariableDescription) + => m_CheckAttributeApplicable(systemDescription, capturedVariableDescription, MethodName); + } - public readonly string MethodName; - public readonly string AttributeName; - readonly CheckAttributeApplicable m_CheckAttributeApplicable; - public bool IsApplicableToCaptured(SystemDescription systemDescription, LambdaCapturedVariableDescription capturedVariableDescription) - => m_CheckAttributeApplicable(systemDescription, capturedVariableDescription, MethodName); - } + public static readonly List AttributesDescriptions = new List + { + new AttributeDescription("WithReadOnly", "Unity.Collections.ReadOnly", CheckHasNativeContainerAttribute), + new AttributeDescription("WithNativeDisableContainerSafetyRestriction", "Unity.Collections.LowLevel.Unsafe.NativeDisableContainerSafetyRestriction", CheckHasNativeContainerAttribute), + new AttributeDescription("WithNativeDisableUnsafePtrRestriction", "Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction", (_, __, ___) => true), + new AttributeDescription("WithNativeDisableParallelForRestriction", "Unity.Collections.NativeDisableParallelForRestriction", CheckHasNativeContainerAttribute), + }; - public static readonly List AttributesDescriptions = new List + static bool CheckHasNativeContainerAttribute(SystemDescription systemDescription, LambdaCapturedVariableDescription capturedVariableDescription, string methodName) + { + const string nativeContainerAttributeName = "Unity.Collections.LowLevel.Unsafe.NativeContainerAttribute"; + if (!(capturedVariableDescription.Symbol is ILocalSymbol localSymbol && localSymbol.Type.HasAttributeOrFieldWithAttribute(nativeContainerAttributeName) || + capturedVariableDescription.Symbol is IParameterSymbol parameterSymbol && parameterSymbol.Type.HasAttributeOrFieldWithAttribute(nativeContainerAttributeName))) { - new AttributeDescription("WithReadOnly", "Unity.Collections.ReadOnly", CheckHasNativeContainerAttribute), - new AttributeDescription("WithNativeDisableContainerSafetyRestriction", "Unity.Collections.LowLevel.Unsafe.NativeDisableContainerSafetyRestriction", CheckHasNativeContainerAttribute), - new AttributeDescription("WithNativeDisableUnsafePtrRestriction", "Unity.Collections.LowLevel.Unsafe.NativeDisableUnsafePtrRestriction", (_, __, ___) => true), - new AttributeDescription("WithNativeDisableParallelForRestriction", "Unity.Collections.NativeDisableParallelForRestriction", CheckHasNativeContainerAttribute), - }; + LambdaJobsErrors.DC0034(systemDescription, capturedVariableDescription.Symbol.Locations.First(), capturedVariableDescription.Symbol.Name, capturedVariableDescription.Type.Name, methodName); + return false; + } + return true; + } - static bool CheckHasNativeContainerAttribute(SystemDescription systemDescription, LambdaCapturedVariableDescription capturedVariableDescription, string methodName) - { - const string nativeContainerAttributeName = "Unity.Collections.LowLevel.Unsafe.NativeContainerAttribute"; - if (!(capturedVariableDescription.Symbol is ILocalSymbol localSymbol && localSymbol.Type.HasAttributeOrFieldWithAttribute(nativeContainerAttributeName) || - capturedVariableDescription.Symbol is IParameterSymbol parameterSymbol && parameterSymbol.Type.HasAttributeOrFieldWithAttribute(nativeContainerAttributeName))) - { - LambdaJobsErrors.DC0034(systemDescription, capturedVariableDescription.Symbol.Locations.First(), capturedVariableDescription.Symbol.Name, capturedVariableDescription.Type.Name, methodName); - return false; - } + public bool SupportsDeallocateOnJobCompletion() + { + if (Type.GetAttributes().Any(attribute => + attribute.AttributeClass.ToFullName() == "Unity.Collections.LowLevel.Unsafe.NativeContainerSupportsDeallocateOnJobCompletionAttribute")) return true; - } - public bool SupportsDeallocateOnJobCompletion() + foreach (var field in Type.GetMembers().OfType()) { - if (Type.GetAttributes().Any(attribute => - attribute.AttributeClass.ToFullName() == "Unity.Collections.LowLevel.Unsafe.NativeContainerSupportsDeallocateOnJobCompletionAttribute")) + if (field.Type.GetAttributes().Any(attribute => attribute.AttributeClass.ToFullName() == "Unity.Collections.LowLevel.Unsafe.NativeContainerSupportsDeallocateOnJobCompletionAttribute")) return true; - - foreach (var field in Type.GetMembers().OfType()) - { - if (field.Type.GetAttributes().Any(attribute => attribute.AttributeClass.ToFullName() == "Unity.Collections.LowLevel.Unsafe.NativeContainerSupportsDeallocateOnJobCompletionAttribute")) - return true; - } - return false; } + return false; + } - public IEnumerable NamesOfAllDisposableMembersIncludingOurselves() - { - var allNames = new List(); + public IEnumerable NamesOfAllDisposableMembersIncludingOurselves() + { + var allNames = new List(); - if (Type.GetMembers().OfType().Any(method => method.Name == "Dispose")) - allNames.Add(VariableFieldName); - else + if (Type.GetMembers().OfType().Any(method => method.Name == "Dispose")) + allNames.Add(VariableFieldName); + else + { + void LocalRecurse(string currentName, ITypeSymbol currentType) { - void LocalRecurse(string currentName, ITypeSymbol currentType) + foreach (var field in currentType.GetMembers().OfType()) { - foreach (var field in currentType.GetMembers().OfType()) - { - if (field.IsStatic || field.Type.TypeKind == TypeKind.Array || field.Type.TypeKind == TypeKind.Pointer) - continue; - var fieldMemberName = $"{currentName}.{field.Name}"; - if (field.Type.GetMembers().OfType().Any(method => method.Name == "Dispose")) - allNames.Add(fieldMemberName); - else - LocalRecurse(fieldMemberName, field.Type); - } + if (field.IsStatic || field.Type.TypeKind == TypeKind.Array || field.Type.TypeKind == TypeKind.Pointer) + continue; + var fieldMemberName = $"{currentName}.{field.Name}"; + if (field.Type.GetMembers().OfType().Any(method => method.Name == "Dispose")) + allNames.Add(fieldMemberName); + else + LocalRecurse(fieldMemberName, field.Type); } - - LocalRecurse(VariableFieldName, Type); } - return allNames; + + LocalRecurse(VariableFieldName, Type); } + return allNames; } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobDescription.cs index 14fe14c..0a87572 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobDescription.cs @@ -1,574 +1,576 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Operations; using Unity.Entities.SourceGen.Common; -using Unity.Entities.SourceGen.SystemGenerator; using Unity.Entities.SourceGen.SystemGenerator.Common; using static Unity.Entities.SourceGen.Common.SourceGenHelpers; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +public class LambdaJobDescription { - public class LambdaJobDescription + public struct BurstSettings { - public struct BurstSettings - { - public BurstFloatMode? BurstFloatMode; - public BurstFloatPrecision? BurstFloatPrecision; - public bool? SynchronousCompilation; - } + public BurstFloatMode? BurstFloatMode; + public BurstFloatPrecision? BurstFloatPrecision; + public bool? SynchronousCompilation; + } - public LambdaParamDescription_EntityCommandBuffer EntityCommandBufferParameter { get; } - public SystemDescription SystemDescription { get; } - public MethodDeclarationSyntax ContainingMethod { get; } - public InvocationExpressionSyntax ContainingInvocationExpression { get; } - public Dictionary> MethodInvocations { get; } - public (bool IsEnabled, BurstSettings Settings) Burst { get; } - public (ScheduleMode Mode, ArgumentSyntax DependencyArgument) Schedule { get; } - - public Query[] WithAllTypes { get; } - public Query[] WithNoneTypes { get ; } - public Query[] WithAnyTypes { get ; } - public Query[] WithDisabledTypes { get ; } - public Query[] WithAbsentTypes { get ; } - public Query[] WithChangeFilterTypes { get ; } - public Query[] WithSharedComponentFilterTypes { get ; } - - public bool HasSharedComponentFilter { get; } - public IReadOnlyCollection WithSharedComponentFilterArgumentSyntaxes { get; } - public IReadOnlyCollection WithStoreEntityQueryInFieldArgumentSyntaxes { get; } - public IReadOnlyCollection WithScheduleGranularityArgumentSyntaxes { get; } - public Location Location { get; } - public EntityQueryOptions EntityQueryOptions { get; } - - public string Name { get; } - public bool Success { get; internal set; } = true; - public string EntityQueryFieldName { get; set; } - public string ExecuteInSystemMethodName => $"{Name}_Execute"; - - bool CanContainReferenceTypes => !Burst.IsEnabled && Schedule.Mode == ScheduleMode.Run; - - public readonly ParenthesizedLambdaExpressionSyntax OriginalLambdaExpression; - public readonly List VariablesCaptured = new List(); - public readonly List DisposeOnJobCompletionVariables = new List(); - public readonly List<(string Name, ISymbol Symbol)> AdditionalVariablesCapturedForScheduling = new List<(string, ISymbol)>(); - - public bool WithStructuralChanges { get; } - public readonly LambdaJobKind LambdaJobKind; - - public readonly ArgumentSyntax WithFilterEntityArray; - - internal readonly List AdditionalFields; - public readonly BlockSyntax RewrittenLambdaBody; - - internal readonly List LambdaParameters = new List(); - public string JobStructName => $"{Name}_Job"; - public string LambdaBodyMethodName => $"{Name}_LambdaBody"; - public bool NeedsJobFunctionPointers => Schedule.Mode == ScheduleMode.Run && (Burst.IsEnabled || LambdaJobKind == LambdaJobKind.Job); - public bool NeedsEntityInQueryIndex => LambdaParameters.OfType().Any(); - public string ChunkBaseEntityIndexFieldName => $"{Name}_ChunkBaseEntityIndexArray"; - - public bool IsForDOTSRuntime => SystemDescription.IsForDotsRuntime; - public bool SafetyChecksEnabled => SystemDescription.IsUnityCollectionChecksEnabled; - public bool DOTSRuntimeProfilerEnabled => SystemDescription.IsDotsRuntimeProfilerEnabled; - public bool ProfilerEnabled => SystemDescription.IsProfilerEnabled || IsForEditor || DevelopmentBuildEnabled; - public bool NeedsUnsafe => ContainingMethod.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.UnsafeKeyword)) || - OriginalLambdaExpression.AncestorOfKindOrDefault() != null; - public bool NeedsTimeData { get; } - - bool NeedsToPassSortKeyToOriginalLambdaBody => EntityCommandBufferParameter is {Playback: {ScheduleMode: ScheduleMode.ScheduleParallel}}; - bool IsDeferredPlaybackSystemSpecified => WithDeferredPlaybackSystemTypes.Any(); - bool IsForEditor => SystemDescription.PreprocessorSymbolNames.Contains("UNITY_EDITOR"); - bool DevelopmentBuildEnabled => SystemDescription.PreprocessorSymbolNames.Contains("DEVELOPMENT_BUILD"); - bool HasManagedParameters => LambdaParameters.OfType().Any(); - bool HasSharedComponentParameters => LambdaParameters.OfType().Any(); - - List WithDeferredPlaybackSystemTypes { get; } - bool WithImmediatePlayback { get; } - - public LambdaJobDescription(SystemDescription systemDescription, LambdaJobsCandidate candidate, MethodDeclarationSyntax containingMethod, int id) + public LambdaParamDescription_EntityCommandBuffer EntityCommandBufferParameter { get; } + public SystemDescription SystemDescription { get; } + public MethodDeclarationSyntax ContainingMethod { get; } + public InvocationExpressionSyntax ContainingInvocationExpression { get; } + public Dictionary> MethodInvocations { get; } + public (bool IsEnabled, BurstSettings Settings) Burst { get; } + public (ScheduleMode Mode, ArgumentSyntax DependencyArgument) Schedule { get; } + + public Query[] WithAllTypes { get; } + public Query[] WithNoneTypes { get ; } + public Query[] WithAnyTypes { get ; } + public Query[] WithDisabledTypes { get ; } + public Query[] WithAbsentTypes { get ; } + public Query[] WithPresentTypes { get ; } + public Query[] WithChangeFilterTypes { get ; } + public Query[] WithSharedComponentFilterTypes { get ; } + public IReadOnlyCollection WithSharedComponentFilterArgumentSyntaxes { get; } + public IReadOnlyCollection WithStoreEntityQueryInFieldArgumentSyntaxes { get; } + public IReadOnlyCollection WithScheduleGranularityArgumentSyntaxes { get; } + public Location Location { get; } + public EntityQueryOptions EntityQueryOptions { get; } + + public string Name { get; } + public bool Success { get; internal set; } = true; + public string EntityQueryFieldName { get; set; } + public string ExecuteInSystemMethodName => $"{Name}_Execute"; + + bool CanContainReferenceTypes => !Burst.IsEnabled && Schedule.Mode == ScheduleMode.Run; + + public readonly ParenthesizedLambdaExpressionSyntax OriginalLambdaExpression; + public readonly List VariablesCaptured = new List(); + public readonly List DisposeOnJobCompletionVariables = new List(); + public readonly List<(string Name, ISymbol Symbol)> AdditionalVariablesCapturedForScheduling = new List<(string, ISymbol)>(); + + public bool WithStructuralChanges { get; } + public readonly LambdaJobKind LambdaJobKind; + + public readonly ArgumentSyntax WithFilterEntityArray; + + internal readonly List AdditionalFields; + public readonly BlockSyntax RewrittenLambdaBody; + + internal readonly List LambdaParameters = new List(); + public string JobStructName => $"{Name}_Job"; + public bool NeedsJobFunctionPointers => Schedule.Mode == ScheduleMode.Run && (Burst.IsEnabled || LambdaJobKind == LambdaJobKind.Job); + public bool NeedsEntityInQueryIndex => LambdaParameters.OfType().Any(); + public string ChunkBaseEntityIndexFieldName => $"{Name}_ChunkBaseEntityIndexArray"; + + public bool IsForDOTSRuntime => SystemDescription.PreprocessorInfo.IsForDotsRuntime; + public bool SafetyChecksEnabled => SystemDescription.PreprocessorInfo.IsUnityCollectionChecksEnabled; + public bool DOTSRuntimeProfilerEnabled => SystemDescription.PreprocessorInfo.IsDotsRuntimeProfilerEnabled; + public bool ProfilerEnabled => + SystemDescription.PreprocessorInfo.IsProfilerEnabled || SystemDescription.PreprocessorInfo.IsForUnityEditor || SystemDescription.PreprocessorInfo.IsDevelopmentBuildEnabled; + public bool NeedsUnsafe => ContainingMethod.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.UnsafeKeyword)) || + OriginalLambdaExpression.AncestorOfKindOrDefault() != null; + public bool NeedsTimeData { get; } + + bool NeedsToPassSortKeyToOriginalLambdaBody => EntityCommandBufferParameter is {Playback: {ScheduleMode: ScheduleMode.ScheduleParallel}}; + bool IsDeferredPlaybackSystemSpecified => WithDeferredPlaybackSystemTypes.Any(); + bool HasManagedParameters => LambdaParameters.OfType().Any(); + bool HasSharedComponentParameters => LambdaParameters.OfType().Any(); + + List WithDeferredPlaybackSystemTypes { get; } + bool WithImmediatePlayback { get; } + + public LambdaJobDescription(SystemDescription systemDescription, LambdaJobsCandidate candidate, MethodDeclarationSyntax containingMethod, int id) + { + try { - try - { - SystemDescription = systemDescription; - Location = candidate.Node.GetLocation(); - ContainingMethod = containingMethod; - MethodInvocations = candidate.MethodInvocations; - Schedule = GetScheduleModeAndDependencyArgument(); - ContainingInvocationExpression = MethodInvocations[Schedule.Mode.ToString()].FirstOrDefault(); - - WithAbsentTypes = - AllTypeArgumentSymbolsOfMethod("WithAbsent").Select(symbol => - new Query - { - TypeSymbol = symbol, - Type = QueryType.Absent, - IsReadOnly = true - }).ToArray(); - WithDisabledTypes = - AllTypeArgumentSymbolsOfMethod("WithDisabled").Select(symbol => - new Query - { - TypeSymbol = symbol, - Type = QueryType.Disabled, - IsReadOnly = true - }).ToArray(); - WithAllTypes = - AllTypeArgumentSymbolsOfMethod("WithAll").Select(symbol => - new Query - { - TypeSymbol = symbol, - Type = QueryType.All, - IsReadOnly = true - }).ToArray(); - WithAnyTypes = - AllTypeArgumentSymbolsOfMethod("WithAny").Select(symbol => - new Query - { - TypeSymbol = symbol, - Type = QueryType.Any, - IsReadOnly = true - } - ).ToArray(); - WithNoneTypes = - AllTypeArgumentSymbolsOfMethod("WithNone").Select(symbol => - new Query - { - TypeSymbol = symbol, - Type = QueryType.None, - IsReadOnly = true - } - ).ToArray(); - WithChangeFilterTypes = - AllTypeArgumentSymbolsOfMethod("WithChangeFilter").Select(symbol => - new Query - { - TypeSymbol = symbol, - Type = QueryType.ChangeFilter, - IsReadOnly = true - } - ).ToArray(); - WithSharedComponentFilterTypes = - AllTypeArgumentSymbolsOfMethod("WithSharedComponentFilter").Select(symbol => - new Query - { - TypeSymbol = symbol, - Type = QueryType.All, - IsReadOnly = true - } - ).ToArray(); + SystemDescription = systemDescription; + Location = candidate.Node.GetLocation(); + ContainingMethod = containingMethod; + MethodInvocations = candidate.MethodInvocations; + Schedule = GetScheduleModeAndDependencyArgument(); + ContainingInvocationExpression = MethodInvocations[Schedule.Mode.ToString()].FirstOrDefault(); + + WithPresentTypes = + AllTypeArgumentSymbolsOfMethod("WithPresent").Select(symbol => + new Query + { + TypeSymbol = symbol, + Type = QueryType.Present, + IsReadOnly = true + }).ToArray(); + WithAbsentTypes = + AllTypeArgumentSymbolsOfMethod("WithAbsent").Select(symbol => + new Query + { + TypeSymbol = symbol, + Type = QueryType.Absent, + IsReadOnly = true + }).ToArray(); + WithDisabledTypes = + AllTypeArgumentSymbolsOfMethod("WithDisabled").Select(symbol => + new Query + { + TypeSymbol = symbol, + Type = QueryType.Disabled, + IsReadOnly = true + }).ToArray(); + WithAllTypes = + AllTypeArgumentSymbolsOfMethod("WithAll").Select(symbol => + new Query + { + TypeSymbol = symbol, + Type = QueryType.All, + IsReadOnly = true + }).ToArray(); + WithAnyTypes = + AllTypeArgumentSymbolsOfMethod("WithAny").Select(symbol => + new Query + { + TypeSymbol = symbol, + Type = QueryType.Any, + IsReadOnly = true + } + ).ToArray(); + WithNoneTypes = + AllTypeArgumentSymbolsOfMethod("WithNone").Select(symbol => + new Query + { + TypeSymbol = symbol, + Type = QueryType.None, + IsReadOnly = true + } + ).ToArray(); + WithChangeFilterTypes = + AllTypeArgumentSymbolsOfMethod("WithChangeFilter").Select(symbol => + new Query + { + TypeSymbol = symbol, + Type = QueryType.ChangeFilter, + IsReadOnly = true + } + ).ToArray(); + WithSharedComponentFilterTypes = + AllTypeArgumentSymbolsOfMethod("WithSharedComponentFilter").Select(symbol => + new Query + { + TypeSymbol = symbol, + Type = QueryType.All, + IsReadOnly = true + } + ).ToArray(); - WithDeferredPlaybackSystemTypes = AllTypeArgumentSymbolsOfMethod("WithDeferredPlaybackSystem"); - WithSharedComponentFilterArgumentSyntaxes = AllArgumentSyntaxesOfMethod("WithSharedComponentFilter").ToArray(); - HasSharedComponentFilter = WithSharedComponentFilterArgumentSyntaxes.Count > 0; - WithStoreEntityQueryInFieldArgumentSyntaxes = AllArgumentSyntaxesOfMethod("WithStoreEntityQueryInField").ToArray(); - WithScheduleGranularityArgumentSyntaxes = AllArgumentSyntaxesOfMethod("WithScheduleGranularity").ToArray(); + WithDeferredPlaybackSystemTypes = AllTypeArgumentSymbolsOfMethod("WithDeferredPlaybackSystem"); + WithSharedComponentFilterArgumentSyntaxes = AllArgumentSyntaxesOfMethod("WithSharedComponentFilter").ToArray(); + WithStoreEntityQueryInFieldArgumentSyntaxes = AllArgumentSyntaxesOfMethod("WithStoreEntityQueryInField").ToArray(); + WithScheduleGranularityArgumentSyntaxes = AllArgumentSyntaxesOfMethod("WithScheduleGranularity").ToArray(); - EntityQueryOptions = GetEntityQueryOptions(); + EntityQueryOptions = GetEntityQueryOptions(); - AdditionalFields = new List(); + AdditionalFields = new List(); + + var methodSymbol = systemDescription.SemanticModel.GetDeclaredSymbol(ContainingMethod); + var stableHashCodeForMethod = GetStableHashCode($"{methodSymbol.ContainingType.ToFullName()}_{methodSymbol.GetMethodAndParamsAsString(this.SystemDescription)}") & 0x7fffffff; + Name = GetName( $"{systemDescription.SystemTypeSyntax.Identifier}_{stableHashCodeForMethod:X}_LambdaJob_{id}"); + LambdaJobKind = candidate.LambdaJobKind; + WithStructuralChanges = MethodInvocations.ContainsKey("WithStructuralChanges"); + WithImmediatePlayback = MethodInvocations.ContainsKey("WithImmediatePlayback"); + WithFilterEntityArray = SingleOptionalArgumentSyntaxOfMethod("WithFilter"); + Burst = GetBurstSettings(); + + var methodInvocationWithLambdaExpression = LambdaJobKind switch + { + LambdaJobKind.Entities => MethodInvocations["ForEach"].FirstOrDefault(), + LambdaJobKind.Job => MethodInvocations["WithCode"].FirstOrDefault(), + _ => throw new InvalidOperationException("LambdaJob does not include required ForEach or WithCode invocation.") + }; + + // Check to see if the lambda expression is not valid, need to do this before we continue analyzing lambda + var lambdaArgument = methodInvocationWithLambdaExpression.ArgumentList.Arguments.FirstOrDefault(); + if (lambdaArgument.Expression is ParenthesizedLambdaExpressionSyntax parenthesizedLambdaExpressionSyntax) + { + OriginalLambdaExpression = parenthesizedLambdaExpressionSyntax; + } + else + { + LambdaJobsErrors.DC0044(SystemDescription, Location, LambdaJobKind); + throw new InvalidDescriptionException(); + } - var methodSymbol = systemDescription.SemanticModel.GetDeclaredSymbol(ContainingMethod); - var stableHashCodeForMethod = GetStableHashCode($"{methodSymbol.ContainingType.ToFullName()}_{methodSymbol.GetMethodAndParamsAsString()}") & 0x7fffffff; - Name = GetName( $"{systemDescription.SystemTypeSyntax.Identifier}_{stableHashCodeForMethod:X}_LambdaJob_{id}"); - LambdaJobKind = candidate.LambdaJobKind; - WithStructuralChanges = MethodInvocations.ContainsKey("WithStructuralChanges"); - WithImmediatePlayback = MethodInvocations.ContainsKey("WithImmediatePlayback"); - WithFilterEntityArray = SingleOptionalArgumentSyntaxOfMethod("WithFilter"); - Burst = GetBurstSettings(); - var methodInvocationWithLambdaExpression = LambdaJobKind switch + // Create parameter description from lambda parameters. + if (OriginalLambdaExpression?.ParameterList.Parameters is {} parameters) + { + LambdaParameters = new List(parameters.Count); + foreach (var param in parameters) { - LambdaJobKind.Entities => MethodInvocations["ForEach"].FirstOrDefault(), - LambdaJobKind.Job => MethodInvocations["WithCode"].FirstOrDefault(), - _ => throw new InvalidOperationException("LambdaJob does not include required ForEach or WithCode invocation.") - }; + var paramDescription = LambdaParamDescription.From(this, param, Name); + if (paramDescription != null) + LambdaParameters.Add(paramDescription); + else + Success = false; + } + } - // Check to see if the lambda expression is not valid, need to do this before we continue analyzing lambda - var lambdaArgument = methodInvocationWithLambdaExpression.ArgumentList.Arguments.FirstOrDefault(); - if (lambdaArgument.Expression is ParenthesizedLambdaExpressionSyntax parenthesizedLambdaExpressionSyntax) + var entityCommandsParameters = + LambdaParameters.OfType().ToArray(); + if (entityCommandsParameters.Any()) + { + var result = VerifyEcbCommandParameter(systemDescription, entityCommandsParameters); + if (result.IsSuccess) { - OriginalLambdaExpression = parenthesizedLambdaExpressionSyntax; + result.Command.Playback = (IsImmediate: WithImmediatePlayback, Schedule.Mode, WithDeferredPlaybackSystemTypes.SingleOrDefault()); + EntityCommandBufferParameter = result.Command; } else { - LambdaJobsErrors.DC0044(SystemDescription, Location, LambdaJobKind); + Success = false; throw new InvalidDescriptionException(); } + } + if (NeedsToPassSortKeyToOriginalLambdaBody) + { + LambdaParameters.Add(new LambdaParamDescription_BatchIndex()); + } - // Create parameter description from lambda parameters. - if (OriginalLambdaExpression?.ParameterList.Parameters is {} parameters) - { - LambdaParameters = new List(parameters.Count); - foreach (var param in parameters) - { - var paramDescription = LambdaParamDescription.From(this, param, Name); - if (paramDescription != null) - LambdaParameters.Add(paramDescription); - else - Success = false; - } - } + var lambdaSyntax = OriginalLambdaExpression.Block ?? (SyntaxNode) OriginalLambdaExpression.ExpressionBody; - var entityCommandsParameters = - LambdaParameters.OfType().ToArray(); - if (entityCommandsParameters.Any()) - { - var result = VerifyEcbCommandParameter(systemDescription, entityCommandsParameters); - if (result.IsSuccess) - { - result.Command.Playback = (IsImmediate: WithImmediatePlayback, Schedule.Mode, WithDeferredPlaybackSystemTypes.SingleOrDefault()); - EntityCommandBufferParameter = result.Command; - } - else - { - Success = false; - throw new InvalidDescriptionException(); - } - } + // Can early out of a lot of analysis if we are only dealing with identifiers that are lambda params + // (no captured variables or additional method calls) + var hasNonParameterIdentifier = lambdaSyntax.DescendantNodes().OfType().Any(identifier => LambdaParameters.All(param => param.Name != identifier.Identifier.ToString())); - if (NeedsToPassSortKeyToOriginalLambdaBody) + if (hasNonParameterIdentifier) + { + // Discover captured variables + // this must not include parameters as they can be captured by inner lambdas + // or variables declared inside of lamba + var dataFlowAnalysis = systemDescription.SemanticModel.AnalyzeDataFlow(lambdaSyntax); + if (dataFlowAnalysis.Succeeded) { - LambdaParameters.Add(new LambdaParamDescription_BatchIndex()); - } - - var lambdaSyntax = OriginalLambdaExpression.Block ?? (SyntaxNode) OriginalLambdaExpression.ExpressionBody; - - // Can early out of a lot of analysis if we are only dealing with identifiers that are lambda params - // (no captured variables or additional method calls) - var hasNonParameterIdentifier = lambdaSyntax.DescendantNodes().OfType().Any(identifier => LambdaParameters.All(param => param.Name != identifier.Identifier.ToString())); - - if (hasNonParameterIdentifier) - { - // Discover captured variables - // this must not include parameters as they can be captured by inner lambdas - // or variables declared inside of lamba - var dataFlowAnalysis = systemDescription.SemanticModel.AnalyzeDataFlow(lambdaSyntax); - if (dataFlowAnalysis.Succeeded) + foreach (var capturedVariable in dataFlowAnalysis.CapturedInside) { - foreach (var capturedVariable in dataFlowAnalysis.CapturedInside) + // Make sure not already captured or a lambda param + if (LambdaParameters.All(param => param.Name != capturedVariable.Name) && + VariablesCaptured.All(param => param.Symbol.Name != capturedVariable.Name)) { - // Make sure not already captured or a lambda param - if (LambdaParameters.All(param => param.Name != capturedVariable.Name) && - VariablesCaptured.All(param => param.Symbol.Name != capturedVariable.Name)) + var capturedVariableDescription = new LambdaCapturedVariableDescription(capturedVariable); + + if (capturedVariableDescription.Type is IErrorTypeSymbol errorTypeSymbol) { - var capturedVariableDescription = new LambdaCapturedVariableDescription(capturedVariable); - VariablesCaptured.Add(capturedVariableDescription); - if (Schedule.Mode != ScheduleMode.Run && !capturedVariableDescription.IsNativeContainer && - dataFlowAnalysis.AlwaysAssigned.Contains(capturedVariable) && dataFlowAnalysis.DataFlowsOut.Contains(capturedVariable)) - { - LambdaJobsErrors.DC0013(SystemDescription, Location, capturedVariable.Name, LambdaJobKind); - Success = false; - } + var location = errorTypeSymbol.Locations.FirstOrDefault(); + if (location == null) // Could be null if type has never been defined + location = lambdaSyntax.GetLocation(); + LambdaJobsErrors.DC0086(SystemDescription, location, errorTypeSymbol); + Success = false; + return; // We will hit further errors, so lets not soldier on } - } - foreach (var localFunc in dataFlowAnalysis.UsedLocalFunctions) - { - var location = OriginalLambdaExpression.GetLocation(); - - // Find first invocation in lambda using that MethodSymbol and return location of it. - foreach (var node in lambdaSyntax.DescendantNodes()) - if (node is InvocationExpressionSyntax { Expression: SimpleNameSyntax nameSyntax } invocation && nameSyntax.Identifier.ValueText == localFunc.Name - && SymbolEqualityComparer.Default.Equals(SystemDescription.SemanticModel.GetSymbolInfo(invocation.Expression).Symbol.OriginalDefinition, localFunc)) - { - location = invocation.GetLocation(); - break; - } - - LambdaJobsErrors.DC0083(SystemDescription, location, LambdaJobKind, Schedule.Mode); - Success = false; + VariablesCaptured.Add(capturedVariableDescription); + if (Schedule.Mode != ScheduleMode.Run && !capturedVariableDescription.IsNativeContainer && + dataFlowAnalysis.AlwaysAssigned.Contains(capturedVariable) && dataFlowAnalysis.DataFlowsOut.Contains(capturedVariable)) + { + LambdaJobsErrors.DC0013(SystemDescription, Location, capturedVariable.Name, LambdaJobKind); + Success = false; + } } } - } - // If we are also using any managed components or doing structural changes, we also need to capture this - if ((HasManagedParameters || HasSharedComponentParameters || WithStructuralChanges) - && VariablesCaptured.All(variable => !variable.IsThis)) - { - VariablesCaptured.Add(new LambdaCapturedVariableDescription(systemDescription.SystemTypeSymbol, true)); + foreach (var localFunc in dataFlowAnalysis.UsedLocalFunctions) + { + var location = OriginalLambdaExpression.GetLocation(); + + // Find first invocation in lambda using that MethodSymbol and return location of it. + foreach (var node in lambdaSyntax.DescendantNodes()) + if (node is InvocationExpressionSyntax { Expression: SimpleNameSyntax nameSyntax } invocation && nameSyntax.Identifier.ValueText == localFunc.Name + && SymbolEqualityComparer.Default.Equals(SystemDescription.SemanticModel.GetSymbolInfo(invocation.Expression).Symbol.OriginalDefinition, localFunc)) + { + location = invocation.GetLocation(); + break; + } + + LambdaJobsErrors.DC0083(SystemDescription, location, LambdaJobKind, Schedule.Mode); + Success = false; + } } + } - // Also captured any variables used in expressions that construct shared component filters - foreach (var sharedComponentFilterArgumentSyntax in WithSharedComponentFilterArgumentSyntaxes) + // If we are also using any managed components or doing structural changes, we also need to capture this + if ((HasManagedParameters || HasSharedComponentParameters || WithStructuralChanges) + && VariablesCaptured.All(variable => !variable.IsThis)) + { + VariablesCaptured.Add(new LambdaCapturedVariableDescription(systemDescription.SystemTypeSymbol, true)); + } + + // Also captured any variables used in expressions that construct shared component filters + foreach (var sharedComponentFilterArgumentSyntax in WithSharedComponentFilterArgumentSyntaxes) + { + foreach (var identifier in sharedComponentFilterArgumentSyntax.DescendantNodes().OfType()) { - foreach (var identifier in sharedComponentFilterArgumentSyntax.DescendantNodes().OfType()) - { - var identifierSymbol = ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, identifier); - if (identifierSymbol.Symbol is ILocalSymbol || identifierSymbol.Symbol is IParameterSymbol && - VariablesCaptured.All(variable => variable.OriginalVariableName != identifier.Identifier.Text)) - AdditionalVariablesCapturedForScheduling.Add((identifier.Identifier.Text, identifierSymbol.Symbol)); - } + var identifierSymbol = ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, identifier); + if (identifierSymbol.Symbol is ILocalSymbol || identifierSymbol.Symbol is IParameterSymbol && + VariablesCaptured.All(variable => variable.OriginalVariableName != identifier.Identifier.Text)) + AdditionalVariablesCapturedForScheduling.Add((identifier.Identifier.Text, identifierSymbol.Symbol)); } + } - foreach (var entityAttribute in LambdaCapturedVariableDescription.AttributesDescriptions) + foreach (var entityAttribute in LambdaCapturedVariableDescription.AttributesDescriptions) + { + foreach (var argumentSyntax in AllArgumentSyntaxesOfMethod(entityAttribute.MethodName)) { - foreach (var argumentSyntax in AllArgumentSyntaxesOfMethod(entityAttribute.MethodName)) + var expression = argumentSyntax.Expression; + if (expression is IdentifierNameSyntax identifier) { - var expression = argumentSyntax.Expression; - if (expression is IdentifierNameSyntax identifier) + var capturedVariable = VariablesCaptured.FirstOrDefault(v => v.Symbol.Name == identifier.Identifier.Text); + if (capturedVariable != null) { - var capturedVariable = VariablesCaptured.FirstOrDefault(v => v.Symbol.Name == identifier.Identifier.Text); - if (capturedVariable != null) - { - if (entityAttribute.IsApplicableToCaptured(SystemDescription, capturedVariable)) - capturedVariable.Attributes.Add(entityAttribute.AttributeName); - else - Success = false; - } + if (entityAttribute.IsApplicableToCaptured(SystemDescription, capturedVariable)) + capturedVariable.Attributes.Add(entityAttribute.AttributeName); else - { - var symbolInfo = systemDescription.SemanticModel.GetSymbolInfo(identifier); - if (symbolInfo.Symbol is ILocalSymbol) - { - // Captured variable is not used - LambdaJobsErrors.DC0035(SystemDescription, argumentSyntax.GetLocation(), identifier.ToString(), entityAttribute.MethodName); - } - else - { - // Not a local variable - LambdaJobsErrors.DC0012(SystemDescription, argumentSyntax.GetLocation(), identifier.ToString(), entityAttribute.MethodName); - } Success = false; - } } else { - LambdaJobsErrors.DC0012(SystemDescription, argumentSyntax.GetLocation(), expression.ToString(), entityAttribute.MethodName); + var symbolInfo = systemDescription.SemanticModel.GetSymbolInfo(identifier); + if (symbolInfo.Symbol is ILocalSymbol) + { + // Captured variable is not used + LambdaJobsErrors.DC0035(SystemDescription, argumentSyntax.GetLocation(), identifier.ToString(), entityAttribute.MethodName); + } + else + { + // Not a local variable + LambdaJobsErrors.DC0012(SystemDescription, argumentSyntax.GetLocation(), identifier.ToString(), entityAttribute.MethodName); + } Success = false; } } + else + { + LambdaJobsErrors.DC0012(SystemDescription, argumentSyntax.GetLocation(), expression.ToString(), entityAttribute.MethodName); + Success = false; + } } + } - // Either add DeallocateOnJobCompletion attributes to variables or add to list of variables that need to be disposed - // (depending of if they support DeallocateOnJobCompletion and if we are running as a job) - foreach (var argumentSyntax in AllArgumentSyntaxesOfMethod("WithDisposeOnCompletion")) + // Either add DeallocateOnJobCompletion attributes to variables or add to list of variables that need to be disposed + // (depending of if they support DeallocateOnJobCompletion and if we are running as a job) + foreach (var argumentSyntax in AllArgumentSyntaxesOfMethod("WithDisposeOnCompletion")) + { + var expression = argumentSyntax.Expression; + if (expression is IdentifierNameSyntax identifier) { - var expression = argumentSyntax.Expression; - if (expression is IdentifierNameSyntax identifier) + var capturedVariable = VariablesCaptured.FirstOrDefault(var => var.Symbol.Name == identifier.Identifier.Text); + if (capturedVariable != null) { - var capturedVariable = VariablesCaptured.FirstOrDefault(var => var.Symbol.Name == identifier.Identifier.Text); - if (capturedVariable != null) + if (Schedule.Mode != ScheduleMode.Run && capturedVariable.SupportsDeallocateOnJobCompletion()) { - if (Schedule.Mode != ScheduleMode.Run && capturedVariable.SupportsDeallocateOnJobCompletion()) - { - capturedVariable.Attributes.Add("Unity.Collections.DeallocateOnJobCompletion"); - } - else - { - DisposeOnJobCompletionVariables.Add(capturedVariable); - } + capturedVariable.Attributes.Add("Unity.Collections.DeallocateOnJobCompletion"); } else { - LambdaJobsErrors.DC0012(SystemDescription, argumentSyntax.GetLocation(), identifier.ToString(), "WithDisposeOnCompletion"); - Success = false; + DisposeOnJobCompletionVariables.Add(capturedVariable); } } else { - LambdaJobsErrors.DC0012(SystemDescription, expression.GetLocation(), expression.ToString(), "WithDisposeOnCompletion"); + LambdaJobsErrors.DC0012(SystemDescription, argumentSyntax.GetLocation(), identifier.ToString(), "WithDisposeOnCompletion"); Success = false; } } - - // Rewrite lambda body and get additional fields that are needed if lambda body is not emitted into system - if (!hasNonParameterIdentifier) - RewrittenLambdaBody = OriginalLambdaExpression.ToBlockSyntax(); else { - SyntaxNode rewrittenLambdaExpression; - var rewriter = new LambdaBodyRewriter(this); - (rewrittenLambdaExpression, AdditionalFields) = rewriter.Rewrite(); - NeedsTimeData = rewriter.NeedsTimeData; - RewrittenLambdaBody = ((ParenthesizedLambdaExpressionSyntax) rewrittenLambdaExpression).ToBlockSyntax(); - } - - // Remove source that has been been disable with preprocessor directives (but remains as disabled text) - RewrittenLambdaBody = (BlockSyntax)new DisabledTextTriviaRemover().Visit(RewrittenLambdaBody); - - // Check to see if we have any references to __this in our rewritten lambda body and we can't contain reference types - // if there is none remove the capture this reference - if (!CanContainReferenceTypes - && RewrittenLambdaBody - .DescendantNodes() - .OfType() - .All(syntax => syntax.Identifier.ToString() != "__this")) - { - VariablesCaptured.RemoveAll(variable => variable.IsThis); + LambdaJobsErrors.DC0012(SystemDescription, expression.GetLocation(), expression.ToString(), "WithDisposeOnCompletion"); + Success = false; } - - this.Verify(); } - catch (InvalidDescriptionException) + + // Rewrite lambda body and get additional fields that are needed if lambda body is not emitted into system + if (!hasNonParameterIdentifier) + RewrittenLambdaBody = OriginalLambdaExpression.ToBlockSyntax(); + else { - Success = false; + SyntaxNode rewrittenLambdaExpression; + var rewriter = new LambdaBodyRewriter(this); + (rewrittenLambdaExpression, AdditionalFields) = rewriter.Rewrite(); + NeedsTimeData = rewriter.NeedsTimeData; + RewrittenLambdaBody = ((ParenthesizedLambdaExpressionSyntax) rewrittenLambdaExpression).ToBlockSyntax(); } - } - private (bool IsSuccess, LambdaParamDescription_EntityCommandBuffer Command) - VerifyEcbCommandParameter(SystemDescription systemDescription, IReadOnlyCollection entityCommandsParameters) - { - bool isSuccess = true; - var ecbCommandParameter = entityCommandsParameters.First(); + // Remove source that has been been disable with preprocessor directives (but remains as disabled text) + RewrittenLambdaBody = (BlockSyntax)new DisabledTextTriviaRemover().Visit(RewrittenLambdaBody); - if (!IsDeferredPlaybackSystemSpecified && !WithImmediatePlayback) // Missing playback instructions + // Check to see if we have any references to __this in our rewritten lambda body and we can't contain reference types + // if there is none remove the capture this reference + if (!CanContainReferenceTypes + && RewrittenLambdaBody + .DescendantNodes() + .OfType() + .All(syntax => syntax.Identifier.ToString() != "__this")) { - LambdaJobsErrors.DC0074(systemDescription, ecbCommandParameter.Syntax.GetLocation()); - isSuccess = false; + VariablesCaptured.RemoveAll(variable => variable.IsThis); } - if (IsDeferredPlaybackSystemSpecified && WithImmediatePlayback) // Conflicting playback instructions - { - LambdaJobsErrors.DC0075(systemDescription, WithDeferredPlaybackSystemTypes.First().Locations.First()); - isSuccess = false; - } + this.Verify(); + } + catch (InvalidDescriptionException) + { + Success = false; + } + } - if (WithDeferredPlaybackSystemTypes.Count > 1) // More than one playback systems specified - { - LambdaJobsErrors.DC0078(systemDescription, WithDeferredPlaybackSystemTypes.First().Locations.First()); - isSuccess = false; - } + private (bool IsSuccess, LambdaParamDescription_EntityCommandBuffer Command) + VerifyEcbCommandParameter(SystemDescription systemDescription, IReadOnlyCollection entityCommandsParameters) + { + bool isSuccess = true; + var ecbCommandParameter = entityCommandsParameters.First(); - if (entityCommandsParameters.Count > 1) - { - LambdaJobsErrors.DC0076(systemDescription, entityCommandsParameters.First().Syntax.GetLocation()); - isSuccess = false; - } + if (!IsDeferredPlaybackSystemSpecified && !WithImmediatePlayback) // Missing playback instructions + { + LambdaJobsErrors.DC0074(systemDescription, ecbCommandParameter.Syntax.GetLocation()); + isSuccess = false; + } - if (WithImmediatePlayback && Schedule.Mode != ScheduleMode.Run) - { - LambdaJobsErrors.DC0077(systemDescription, ecbCommandParameter.Syntax.GetLocation()); - isSuccess = false; - } + if (IsDeferredPlaybackSystemSpecified && WithImmediatePlayback) // Conflicting playback instructions + { + LambdaJobsErrors.DC0075(systemDescription, WithDeferredPlaybackSystemTypes.First().Locations.First()); + isSuccess = false; + } - if (isSuccess) - { - ecbCommandParameter = entityCommandsParameters.Single(); - } + if (WithDeferredPlaybackSystemTypes.Count > 1) // More than one playback systems specified + { + LambdaJobsErrors.DC0078(systemDescription, WithDeferredPlaybackSystemTypes.First().Locations.First()); + isSuccess = false; + } - return (IsSuccess: isSuccess, ecbCommandParameter); + if (entityCommandsParameters.Count > 1) + { + LambdaJobsErrors.DC0076(systemDescription, entityCommandsParameters.First().Syntax.GetLocation()); + isSuccess = false; } - ArgumentSyntax SingleOptionalArgumentSyntaxOfMethod(string methodName) + if (WithImmediatePlayback && Schedule.Mode != ScheduleMode.Run) { - return - !MethodInvocations.TryGetValue(methodName, out var invocations) - ? null - : invocations.Select(methodInvocation => methodInvocation.ArgumentList.Arguments.First()).FirstOrDefault(arg => arg != null); + LambdaJobsErrors.DC0077(systemDescription, ecbCommandParameter.Syntax.GetLocation()); + isSuccess = false; } - (bool IsEnabled, BurstSettings Settings) GetBurstSettings() + if (isSuccess) { - if (MethodInvocations.ContainsKey("WithoutBurst") || WithStructuralChanges) - return (false, default); + ecbCommandParameter = entityCommandsParameters.Single(); + } - if (!MethodInvocations.TryGetValue("WithBurst", out var burstInvocations)) - return (true, default); + return (IsSuccess: isSuccess, ecbCommandParameter); + } - BurstFloatMode? burstFloatMode = null; - BurstFloatPrecision? burstFloatPrecision = null; - bool? synchronousCompilation = null; - var invocation = burstInvocations.First(); + ArgumentSyntax SingleOptionalArgumentSyntaxOfMethod(string methodName) + { + return + !MethodInvocations.TryGetValue(methodName, out var invocations) + ? null + : invocations.Select(methodInvocation => methodInvocation.ArgumentList.Arguments.First()).FirstOrDefault(arg => arg != null); + } - // handle both named and unnamed arguments - var argIndex = 0; - var invalidBurstArg = false; - foreach (var argument in invocation.ArgumentList.Arguments) - { - var argumentName = argument.DescendantNodes().OfType().FirstOrDefault()?.Name; - if (argumentName != null) - { - argIndex = argumentName.Identifier.ValueText switch - { - "floatMode" => 0, - "floatPrecision" => 1, - "synchronousCompilation" => 2, - _ => argIndex - }; - } + (bool IsEnabled, BurstSettings Settings) GetBurstSettings() + { + if (MethodInvocations.ContainsKey("WithoutBurst") || WithStructuralChanges) + return (false, default); - var argValue = argument.Expression.ToString(); - switch (argIndex) - { - case 0: - if (TryParseQualifiedEnumValue(argValue, out BurstFloatMode mode)) - burstFloatMode = mode; - else - invalidBurstArg = true; - break; - case 1: - if (TryParseQualifiedEnumValue(argValue, out BurstFloatPrecision precision)) - burstFloatPrecision = precision; - else - invalidBurstArg = true; - break; - case 2: - if (bool.TryParse(argValue, out var synchronous)) - synchronousCompilation = synchronous; - else - invalidBurstArg = true; - break; - } + if (!MethodInvocations.TryGetValue("WithBurst", out var burstInvocations)) + return (true, default); + + BurstFloatMode? burstFloatMode = null; + BurstFloatPrecision? burstFloatPrecision = null; + bool? synchronousCompilation = null; + var invocation = burstInvocations.First(); - argIndex++; + // handle both named and unnamed arguments + var argIndex = 0; + var invalidBurstArg = false; + foreach (var argument in invocation.ArgumentList.Arguments) + { + var argumentName = argument.DescendantNodes().OfType().FirstOrDefault()?.Name; + if (argumentName != null) + { + argIndex = argumentName.Identifier.ValueText switch + { + "floatMode" => 0, + "floatPrecision" => 1, + "synchronousCompilation" => 2, + _ => argIndex + }; } - if (invalidBurstArg) + var argValue = argument.Expression.ToString(); + switch (argIndex) { - LambdaJobsErrors.DC0008(SystemDescription, invocation.GetLocation(), "WithBurst"); - Success = false; + case 0: + if (TryParseQualifiedEnumValue(argValue, out BurstFloatMode mode)) + burstFloatMode = mode; + else + invalidBurstArg = true; + break; + case 1: + if (TryParseQualifiedEnumValue(argValue, out BurstFloatPrecision precision)) + burstFloatPrecision = precision; + else + invalidBurstArg = true; + break; + case 2: + if (bool.TryParse(argValue, out var synchronous)) + synchronousCompilation = synchronous; + else + invalidBurstArg = true; + break; } - return - (true, - new BurstSettings - { - SynchronousCompilation = synchronousCompilation, - BurstFloatMode = burstFloatMode, - BurstFloatPrecision = burstFloatPrecision - }); + argIndex++; } - EntityQueryOptions GetEntityQueryOptions() + if (invalidBurstArg) { - var options = EntityQueryOptions.Default; - - if (!MethodInvocations.TryGetValue("WithEntityQueryOptions", out var invocations)) - { - return options; - } + LambdaJobsErrors.DC0008(SystemDescription, invocation.GetLocation(), "WithBurst"); + Success = false; + } - foreach (var invocation in invocations) - { - var entityQueryOptionArgument = invocation.ArgumentList.Arguments.ElementAtOrDefault(0); - if (entityQueryOptionArgument == null) + return + (true, + new BurstSettings { - continue; - } + SynchronousCompilation = synchronousCompilation, + BurstFloatMode = burstFloatMode, + BurstFloatPrecision = burstFloatPrecision + }); + } - EntityQueryOptions option; - var argExpr = entityQueryOptionArgument.Expression; + EntityQueryOptions GetEntityQueryOptions() + { + var options = EntityQueryOptions.Default; - while (argExpr is BinaryExpressionSyntax binSyntax) - { - if (TryParseQualifiedEnumValue(binSyntax.Right.ToString(), out option)) - { - options |= option; - } else - { - // !!! Need a test for this error - SystemGeneratorErrors.DC0064(SystemDescription, invocation.GetLocation()); - } + if (!MethodInvocations.TryGetValue("WithEntityQueryOptions", out var invocations)) + { + return options; + } - argExpr = binSyntax.Left; - } + foreach (var invocation in invocations) + { + var entityQueryOptionArgument = invocation.ArgumentList.Arguments.ElementAtOrDefault(0); + if (entityQueryOptionArgument == null) + { + continue; + } - if (TryParseQualifiedEnumValue(argExpr.ToString(), out option)) + EntityQueryOptions option; + var argExpr = entityQueryOptionArgument.Expression; + + while (argExpr is BinaryExpressionSyntax binSyntax) + { + if (TryParseQualifiedEnumValue(binSyntax.Right.ToString(), out option)) { options |= option; } else @@ -576,116 +578,180 @@ EntityQueryOptions GetEntityQueryOptions() // !!! Need a test for this error SystemGeneratorErrors.DC0064(SystemDescription, invocation.GetLocation()); } - } - return options; - } - (ScheduleMode Mode, ArgumentSyntax Dependency) GetScheduleModeAndDependencyArgument() - { - if (MethodInvocations.ContainsKey("Run")) - return (ScheduleMode.Run, default); - if (MethodInvocations.TryGetValue("Schedule", out var schedulingInvocations)) - return (ScheduleMode.Schedule, schedulingInvocations.First().ArgumentList.Arguments.SingleOrDefault()); - if (MethodInvocations.TryGetValue("ScheduleParallel", out var schedulingParallelInvocations)) - return (ScheduleMode.ScheduleParallel, schedulingParallelInvocations.First().ArgumentList.Arguments.SingleOrDefault()); + argExpr = binSyntax.Left; + } - Success = false; - throw new InvalidDescriptionException(); + if (TryParseQualifiedEnumValue(argExpr.ToString(), out option)) + { + options |= option; + } else + { + // !!! Need a test for this error + SystemGeneratorErrors.DC0064(SystemDescription, invocation.GetLocation()); + } } + return options; + } + + (ScheduleMode Mode, ArgumentSyntax Dependency) GetScheduleModeAndDependencyArgument() + { + if (MethodInvocations.ContainsKey("Run")) + return (ScheduleMode.Run, default); + if (MethodInvocations.TryGetValue("Schedule", out var schedulingInvocations)) + return (ScheduleMode.Schedule, schedulingInvocations.First().ArgumentList.Arguments.SingleOrDefault()); + if (MethodInvocations.TryGetValue("ScheduleParallel", out var schedulingParallelInvocations)) + return (ScheduleMode.ScheduleParallel, schedulingParallelInvocations.First().ArgumentList.Arguments.SingleOrDefault()); + + Success = false; + throw new InvalidDescriptionException(); + } - // In the case of some copied source (lambda bodies in particular), we need to ensure that we remove any disabled text. - // If we emit this back as source, the metadata marking the source as disabled text will be ignored and the it will include the - // disabled text as valid source. - class DisabledTextTriviaRemover : CSharpSyntaxRewriter + // In the case of some copied source (lambda bodies in particular), we need to ensure that we remove any disabled text. + // If we emit this back as source, the metadata marking the source as disabled text will be ignored and the it will include the + // disabled text as valid source. + class DisabledTextTriviaRemover : CSharpSyntaxRewriter + { + static bool IsTriviaToRemove(SyntaxTrivia syntaxTrivia) => + (syntaxTrivia.Kind() == SyntaxKind.DisabledTextTrivia || + syntaxTrivia.Kind() == SyntaxKind.IfDirectiveTrivia || + syntaxTrivia.Kind() == SyntaxKind.ElseDirectiveTrivia || + syntaxTrivia.Kind() == SyntaxKind.ElifDirectiveTrivia || + syntaxTrivia.Kind() == SyntaxKind.EndIfDirectiveTrivia); + + public override SyntaxToken VisitToken(SyntaxToken token) { - static bool IsTriviaToRemove(SyntaxTrivia syntaxTrivia) => - (syntaxTrivia.Kind() == SyntaxKind.DisabledTextTrivia || - syntaxTrivia.Kind() == SyntaxKind.IfDirectiveTrivia || - syntaxTrivia.Kind() == SyntaxKind.ElseDirectiveTrivia || - syntaxTrivia.Kind() == SyntaxKind.ElifDirectiveTrivia || - syntaxTrivia.Kind() == SyntaxKind.EndIfDirectiveTrivia); - - public override SyntaxToken VisitToken(SyntaxToken token) - { - return base.VisitToken(token) - .WithLeadingTrivia(token.LeadingTrivia.Where(trivia => !IsTriviaToRemove(trivia))) - .WithTrailingTrivia(token.TrailingTrivia.Where(trivia => !IsTriviaToRemove(trivia))); - } + return base.VisitToken(token) + .WithLeadingTrivia(token.LeadingTrivia.Where(trivia => !IsTriviaToRemove(trivia))) + .WithTrailingTrivia(token.TrailingTrivia.Where(trivia => !IsTriviaToRemove(trivia))); } + } + + IEnumerable AllArgumentSyntaxesOfMethod(string methodName) + { + if (MethodInvocations.TryGetValue(methodName, out var invocations)) + foreach (var inv in invocations) + foreach (var arg in inv.ArgumentList.Arguments) + yield return arg; + } - IEnumerable AllArgumentSyntaxesOfMethod(string methodName) + List AllTypeArgumentSymbolsOfMethod(string methodName) + { + var result = new List(); + if (!MethodInvocations.ContainsKey(methodName)) { - if (MethodInvocations.TryGetValue(methodName, out var invocations)) - foreach (var inv in invocations) - foreach (var arg in inv.ArgumentList.Arguments) - yield return arg; + return result; } - List AllTypeArgumentSymbolsOfMethod(string methodName) + foreach (var methodInvocation in MethodInvocations[methodName]) { - var result = new List(); - if (!MethodInvocations.ContainsKey(methodName)) + var symbol = (IMethodSymbol)SystemDescription.SemanticModel.GetSymbolInfo(methodInvocation).Symbol; + + // We can fail to get the symbol here, in that case we don't have access to the type + // this will be reported by Roslyn with + if (symbol == null) { - return result; + Success = false; + continue; } - foreach (var methodInvocation in MethodInvocations[methodName]) + foreach (var argumentType in symbol.TypeArguments.OfType()) { - var symbol = (IMethodSymbol)SystemDescription.SemanticModel.GetSymbolInfo(methodInvocation).Symbol; + SystemGeneratorErrors.DC0051(SystemDescription, Location, argumentType.Name, methodName); + Success = false; + } - // We can fail to get the symbol here, in that case we don't have access to the type - // this will be reported by Roslyn with - if (symbol == null) + foreach (var argumentType in symbol.TypeArguments.OfType()) + { + if (argumentType.IsGenericType) { + SystemGeneratorErrors.DC0051(SystemDescription, Location, argumentType.Name, methodName); Success = false; continue; } - foreach (var argumentType in symbol.TypeArguments.OfType()) - { - SystemGeneratorErrors.DC0051(SystemDescription, Location, argumentType.Name, methodName); - Success = false; - } + result.Add(argumentType); + } + } - foreach (var argumentType in symbol.TypeArguments.OfType()) - { - if (argumentType.IsGenericType) - { - SystemGeneratorErrors.DC0051(SystemDescription, Location, argumentType.Name, methodName); - Success = false; - continue; - } + return result; + } - result.Add(argumentType); - } - } + string GetName(string defaultName) + { + if (!MethodInvocations.TryGetValue("WithName", out var withNameInvocations)) + return defaultName; - return result; + var invocation = withNameInvocations.First(); + var literalArgument = invocation.ArgumentList.Arguments.FirstOrDefault()?.DescendantNodes().OfType().FirstOrDefault(); + + if (literalArgument == null) + { + LambdaJobsErrors.DC0008(SystemDescription, invocation.GetLocation(), "WithName"); + Success = false; + return defaultName; + } + + var customName = literalArgument.Token.ValueText; + if (!IsValidLambdaName(customName)) + { + LambdaJobsErrors.DC0043(SystemDescription, Location, customName); + return defaultName; } - string GetName(string defaultName) + return literalArgument.Token.ValueText; + + static bool IsValidLambdaName(string jobName) { - if (!MethodInvocations.TryGetValue("WithName", out var withNameInvocations)) - return defaultName; + if (jobName.Length == 0) + return false; + if (char.IsDigit(jobName[0])) + return false; + if (jobName.Any(t => t != '_' && !char.IsLetterOrDigit(t))) + return false; + + // names with __ are reserved for the compiler by convention + return !jobName.Contains("__"); + } + } - var invocation = withNameInvocations.First(); - var literalArgument = invocation.ArgumentList.Arguments.FirstOrDefault()?.DescendantNodes().OfType().FirstOrDefault(); + internal void AppendSchedulingInvocationTo(StringBuilder stringBuilder) + { + foreach (var leadingTrivia in ContainingInvocationExpression.GetLeadingTrivia()) + // Ensure neat indentation + if (leadingTrivia.Kind() == SyntaxKind.WhitespaceTrivia) + stringBuilder.Append(leadingTrivia); - if (literalArgument == null) - { - LambdaJobsErrors.DC0008(SystemDescription, invocation.GetLocation(), "WithName"); - Success = false; - return defaultName; - } + stringBuilder.Append($"{ExecuteInSystemMethodName}("); + + var args = GetExecuteMethodArgs().ToArray(); + for (var index = 0; index < args.Length; index++) + { + stringBuilder.Append(args[index]); + + if (index < args.Length - 1) + stringBuilder.Append(", "); + } + stringBuilder.Append(")"); - var customName = literalArgument.Token.ValueText; - if (!customName.IsValidLambdaName()) + IEnumerable GetExecuteMethodArgs() + { + var argStrings = new HashSet(); + foreach (var variable in VariablesCaptured) { - LambdaJobsErrors.DC0043(SystemDescription, Location, customName); - return defaultName; + if (!variable.IsThis) + argStrings.Add(Schedule.Mode == ScheduleMode.Run && variable.IsWritable + ? $"ref {variable.OriginalVariableName}" + : variable.OriginalVariableName); } - return literalArgument.Token.ValueText; + if (Schedule.DependencyArgument != null) + argStrings.Add(Schedule.DependencyArgument.ToString()); + + foreach (var argument in AdditionalVariablesCapturedForScheduling) + argStrings.Add(argument.Name); + + return argStrings; } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobVerification.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobVerification.cs index 91237a7..cd6a1e4 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobVerification.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobVerification.cs @@ -4,286 +4,302 @@ using Unity.Entities.SourceGen.Common; using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +static class LambdaJobsVerification { - static class LambdaJobsVerification + public static void Verify(this LambdaJobDescription description) { - public static void Verify(this LambdaJobDescription description) + // If our lambda expression is badly formed, early out as we will likely flag other false errors (capturing of this) + if (!VerifyLambdaExpression(description)) { - // If our lambda expression is badly formed, early out as we will likely flag other false errors (capturing of this) - if (!VerifyLambdaExpression(description)) - { - description.Success = false; - return; - } - - // Fail if we have other issues with captured variables or are capturing a reference type in burst or non-run - foreach (var variableCaptured in description.VariablesCaptured) - { - if (variableCaptured.Type.TypeKind == TypeKind.Error) - description.Success = false; - else if (variableCaptured.Type.IsReferenceType && - (description.Burst.IsEnabled || description.Schedule.Mode != ScheduleMode.Run)) - { - LambdaJobsErrors.DC0004(description.SystemDescription, description.Location, - variableCaptured.OriginalVariableName, description.LambdaJobKind); - description.Success = false; - } - } + description.Success = false; + return; + } - if (description.LambdaJobKind == LambdaJobKind.Job && description.WithStructuralChanges) - { - LambdaJobsErrors.DC0057(description.SystemDescription, description.Location); + // Fail if we have other issues with captured variables or are capturing a reference type in burst or non-run + foreach (var variableCaptured in description.VariablesCaptured) + { + if (variableCaptured.Type.TypeKind == TypeKind.Error) description.Success = false; - } - - if (!VerifyDC0073(description)) + else if (variableCaptured.Type.IsReferenceType && + (description.Burst.IsEnabled || description.Schedule.Mode != ScheduleMode.Run)) { + LambdaJobsErrors.DC0004(description.SystemDescription, description.Location, + variableCaptured.OriginalVariableName, description.LambdaJobKind); description.Success = false; } } - static bool VerifyLambdaExpression(LambdaJobDescription description) + if (description.LambdaJobKind == LambdaJobKind.Job && description.WithStructuralChanges) + { + LambdaJobsErrors.DC0057(description.SystemDescription, description.Location); + description.Success = false; + } + + if (!VerifyDC0073(description)) { - var success = true; + description.Success = false; + } + } + + static bool VerifyLambdaExpression(LambdaJobDescription description) + { + var success = true; - // Check method calls inside lambda for: - // a) if any structural changes are made - // b) nested Entities.ForEach - foreach (var invocationExpression in - description.OriginalLambdaExpression.DescendantNodes().OfType()) + // Check method calls inside lambda for: + // a) if any structural changes are made + // b) nested Entities.ForEach + foreach (var invocationExpression in + description.OriginalLambdaExpression.DescendantNodes().OfType()) + { + if ((!description.WithStructuralChanges || description.Schedule.Mode != ScheduleMode.Run) && + DoesPerformStructuralChange(invocationExpression, description.SystemDescription.SemanticModel)) { - if ((!description.WithStructuralChanges || description.Schedule.Mode != ScheduleMode.Run) && - invocationExpression.DoesPerformStructuralChange(description.SystemDescription.SemanticModel)) - { - LambdaJobsErrors.DC0027(description.SystemDescription, invocationExpression.GetLocation(), description.LambdaJobKind); - success = false; - } + LambdaJobsErrors.DC0027(description.SystemDescription, invocationExpression.GetLocation(), description.LambdaJobKind); + success = false; } + } - foreach (var methodInvocations in description.MethodInvocations) + foreach (var methodInvocations in description.MethodInvocations) + { + if (methodInvocations.Key != "ForEach" && methodInvocations.Key != "WithCode") { - if (methodInvocations.Key != "ForEach" && methodInvocations.Key != "WithCode") + foreach (var methodInvocation in methodInvocations.Value.Where(methodInvocation => methodInvocation.ContainsDynamicCode())) { - foreach (var methodInvocation in methodInvocations.Value.Where(methodInvocation => methodInvocation.ContainsDynamicCode())) - { - LambdaJobsErrors.DC0010(description.SystemDescription, methodInvocation.GetLocation(), methodInvocations.Key, description.LambdaJobKind); - description.Success = false; - } + LambdaJobsErrors.DC0010(description.SystemDescription, methodInvocation.GetLocation(), methodInvocations.Key, description.LambdaJobKind); + description.Success = false; } } + } - foreach (var methods in description.MethodInvocations.Where(methods => methods.Value.Count > 1)) - { - var methodInvocationSymbol = description.SystemDescription.SemanticModel.GetSymbolInfo(methods.Value.First()).Symbol; - if (!methodInvocationSymbol.HasAttribute( + foreach (var methods in description.MethodInvocations.Where(methods => methods.Value.Count > 1)) + { + var methodInvocationSymbol = description.SystemDescription.SemanticModel.GetSymbolInfo(methods.Value.First()).Symbol; + if (!methodInvocationSymbol.HasAttribute( "Unity.Entities.LambdaJobDescriptionConstructionMethods.AllowMultipleInvocationsAttribute")) - { - LambdaJobsErrors.DC0009(description.SystemDescription, description.Location, methods.Key); - description.Success = false; - } + { + LambdaJobsErrors.DC0009(description.SystemDescription, description.Location, methods.Key); + description.Success = false; } + } - foreach (var node in description.OriginalLambdaExpression.DescendantNodes()) + foreach (var node in description.OriginalLambdaExpression.DescendantNodes()) + { + switch (node) { - switch (node) - { - case AnonymousFunctionExpressionSyntax anonymousFunction: - // Give better error if user tries to nest EFE/JWC. - if (anonymousFunction is ParenthesizedLambdaExpressionSyntax - && node.Parent.Parent.Parent is InvocationExpressionSyntax invocation) // read node.Parent.Parent.Parent as lambda.argument.argumentlist.invocation - { - var theInvocationSymbol = description.SystemDescription.SemanticModel.GetSymbolInfo(invocation.Expression).Symbol; + case AnonymousFunctionExpressionSyntax anonymousFunction: + // Give better error if user tries to nest EFE/JWC. + if (anonymousFunction is ParenthesizedLambdaExpressionSyntax + && node.Parent.Parent.Parent is InvocationExpressionSyntax invocation) // read node.Parent.Parent.Parent as lambda.argument.argumentlist.invocation + { + var theInvocationSymbol = description.SystemDescription.SemanticModel.GetSymbolInfo(invocation.Expression).Symbol; - var isForEach = theInvocationSymbol is IMethodSymbol {Name: "ForEach"} methodSymbolForEach // Is ForEach method called as LambdaForEach - && methodSymbolForEach.ContainingType.Is("LambdaForEachDescriptionConstructionMethods"); + var isForEach = theInvocationSymbol is IMethodSymbol {Name: "ForEach"} methodSymbolForEach // Is ForEach method called as LambdaForEach + && methodSymbolForEach.ContainingType.Is("LambdaForEachDescriptionConstructionMethods"); - var isJobWithCode = theInvocationSymbol is IMethodSymbol {Name: "WithCode"} methodSymbolWithCode // Is WithCode method called as LambdaSingleJob - && methodSymbolWithCode.ContainingType.Is("LambdaSingleJobDescriptionConstructionMethods"); + var isJobWithCode = theInvocationSymbol is IMethodSymbol {Name: "WithCode"} methodSymbolWithCode // Is WithCode method called as LambdaSingleJob + && methodSymbolWithCode.ContainingType.Is("LambdaSingleJobDescriptionConstructionMethods"); - if (isForEach || isJobWithCode) - { - LambdaJobsErrors.DC0029(description.SystemDescription, invocation.GetLocation(), description.LambdaJobKind); - success = false; - break; - } + if (isForEach || isJobWithCode) + { + LambdaJobsErrors.DC0029(description.SystemDescription, invocation.GetLocation(), description.LambdaJobKind); + success = false; + break; } + } - LambdaJobsErrors.DC0084(description.SystemDescription, anonymousFunction.GetLocation(), description.LambdaJobKind, description.Schedule.Mode); - description.Success = false; - break; - case LocalFunctionStatementSyntax localFunction: - LambdaJobsErrors.DC0085(description.SystemDescription, localFunction.GetLocation(), description.LambdaJobKind, description.Schedule.Mode); - description.Success = false; - break; - } - } - - foreach (var storeInQuerySyntax in description.WithStoreEntityQueryInFieldArgumentSyntaxes) - { - if (!(storeInQuerySyntax.Expression is IdentifierNameSyntax storeInQueryIdentifier) || !(description.SystemDescription.SemanticModel.GetSymbolInfo(storeInQueryIdentifier).Symbol is IFieldSymbol)) - { - LambdaJobsErrors.DC0031(description.SystemDescription, description.Location); - description.Success = false; - } - } - - if (description.Burst.IsEnabled || description.Schedule.Mode is ScheduleMode.Schedule || description.Schedule.Mode is ScheduleMode.ScheduleParallel) - { - foreach (var param in description.LambdaParameters.OfType()) - { - LambdaJobsErrors.DC0223(description.SystemDescription, description.Location, param.Name, true, description.LambdaJobKind); + LambdaJobsErrors.DC0084(description.SystemDescription, anonymousFunction.GetLocation(), description.LambdaJobKind, description.Schedule.Mode); description.Success = false; - } - foreach (var param in description.LambdaParameters.OfType()) - { - LambdaJobsErrors.DC0223(description.SystemDescription, description.Location, param.TypeSymbol.Name, false, description.LambdaJobKind); + break; + case LocalFunctionStatementSyntax localFunction: + LambdaJobsErrors.DC0085(description.SystemDescription, localFunction.GetLocation(), description.LambdaJobKind, description.Schedule.Mode); description.Success = false; - } + break; } + } - var duplicateTypes = - description - .LambdaParameters - .Where(desc => !desc.AllowDuplicateTypes && !desc.IsSourceGeneratedParam) - .FindDuplicatesBy(desc => desc.TypeSymbol.ToFullName()); - - foreach (var duplicate in duplicateTypes) + foreach (var storeInQuerySyntax in description.WithStoreEntityQueryInFieldArgumentSyntaxes) + { + if (!(storeInQuerySyntax.Expression is IdentifierNameSyntax storeInQueryIdentifier) || !(description.SystemDescription.SemanticModel.GetSymbolInfo(storeInQueryIdentifier).Symbol is IFieldSymbol)) { - LambdaJobsErrors.DC0070(description.SystemDescription, description.Location, duplicate.TypeSymbol); + LambdaJobsErrors.DC0031(description.SystemDescription, description.Location); description.Success = false; } + } - foreach (var param in description.LambdaParameters.OfType().Where(param => string.IsNullOrEmpty(param.Syntax.GetModifierString()))) + if (description.Burst.IsEnabled || description.Schedule.Mode is ScheduleMode.Schedule || description.Schedule.Mode is ScheduleMode.ScheduleParallel) + { + foreach (var param in description.LambdaParameters.OfType()) { - LambdaJobsErrors.DC0055(description.SystemDescription, description.Location, param.Name, description.LambdaJobKind); + LambdaJobsErrors.DC0223(description.SystemDescription, description.Location, param.Name, true, description.LambdaJobKind); + description.Success = false; } - - foreach (var param in description.LambdaParameters.OfType().Where(param => param.IsByRef())) + foreach (var param in description.LambdaParameters.OfType()) { - if (param.IsByRef()) - { - LambdaJobsErrors.DC0024(description.SystemDescription, description.Location, param.Name); - description.Success = false; - } + LambdaJobsErrors.DC0223(description.SystemDescription, description.Location, param.TypeSymbol.Name, false, description.LambdaJobKind); + description.Success = false; } + } - foreach (var param in description.LambdaParameters.OfType().Where(param => param.IsByRef())) + var duplicateTypes = + description + .LambdaParameters + .Where(desc => !desc.AllowDuplicateTypes && !desc.IsSourceGeneratedParam) + .FindDuplicatesBy(desc => desc.TypeSymbol.ToFullName()); + + foreach (var duplicate in duplicateTypes) + { + LambdaJobsErrors.DC0070(description.SystemDescription, description.Location, duplicate.TypeSymbol); + description.Success = false; + } + + foreach (var param in description.LambdaParameters.OfType().Where(param => string.IsNullOrEmpty(param.Syntax.GetModifierString()))) + { + LambdaJobsErrors.DC0055(description.SystemDescription, description.Location, param.Name, description.LambdaJobKind); + } + + foreach (var param in description.LambdaParameters.OfType().Where(param => param.IsByRef())) + { + if (param.IsByRef()) { - LambdaJobsErrors.DC0020(description.SystemDescription, description.Location, param.TypeSymbol.Name); + LambdaJobsErrors.DC0024(description.SystemDescription, description.Location, param.Name); description.Success = false; } + } - foreach (var param in description.LambdaParameters.OfType()) + foreach (var param in description.LambdaParameters.OfType().Where(param => param.IsByRef())) + { + LambdaJobsErrors.DC0020(description.SystemDescription, description.Location, param.TypeSymbol.Name); + description.Success = false; + } + + foreach (var param in description.LambdaParameters.OfType()) + { + if (param.TypeSymbol is INamedTypeSymbol namedTypeSymbol) { - if (param.TypeSymbol is INamedTypeSymbol namedTypeSymbol) + foreach (var typeArg in namedTypeSymbol.TypeArguments.OfType().Where( + typeArg => typeArg is { } namedTypeArg && namedTypeArg.TypeArguments.Any())) { - foreach (var typeArg in namedTypeSymbol.TypeArguments.OfType().Where( - typeArg => typeArg is { } namedTypeArg && namedTypeArg.TypeArguments.Any())) - { - LambdaJobsErrors.DC0050(description.SystemDescription, description.Location, typeArg.Name, description.LambdaJobKind); - description.Success = false; - } + LambdaJobsErrors.DC0050(description.SystemDescription, description.Location, typeArg.Name, description.LambdaJobKind); + description.Success = false; } } + } - description.Success &= - QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithAllTypes, invokedMethodName: "WithAll"); - description.Success &= - QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithNoneTypes, invokedMethodName: "WithNone"); - description.Success &= - QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithAnyTypes, invokedMethodName: "WithAny"); - description.Success &= - QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithDisabledTypes, invokedMethodName: "WithDisabled"); - description.Success &= - QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithAbsentTypes, invokedMethodName: "WithAbsent"); - - description.Success &= - QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithAllTypes); - description.Success &= - QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithAnyTypes); - description.Success &= - QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithNoneTypes); - description.Success &= - QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithDisabledTypes); - description.Success &= - QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithAbsentTypes); - - description.Success &= - QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithAbsentTypes, description.WithDisabledTypes, "WithAbsent", "WithDisabled"); - description.Success &= - QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithAbsentTypes, description.WithAllTypes, "WithAbsent", "WithAll"); - description.Success &= - QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithAbsentTypes, description.WithAnyTypes, "WithAbsent", "WithAny"); - description.Success &= - QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithNoneTypes, description.WithAllTypes, "WithNone", "WithAll"); - description.Success &= - QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithNoneTypes, description.WithAnyTypes, "WithNone", "WithAny"); - description.Success &= - QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithAnyTypes, description.WithAllTypes, "WithAny", "WithAll"); - - var lambdaParameters = - description.LambdaParameters - .Where(param => param.IsQueryableType) - .Select(param => - new Query - { - TypeSymbol = param.TypeSymbol, - Type = QueryType.All, - IsReadOnly = param.QueryTypeIsReadOnly() - }) - .ToArray(); - - description.Success &= QueryVerification.VerifyNoMutuallyExclusiveQueries( - description.SystemDescription, - description.Location, - description.WithNoneTypes, - lambdaParameters, - "WithNone", - "lambda parameter", - compareTypeSymbolsOnly: true); - - description.Success &= QueryVerification.VerifyNoMutuallyExclusiveQueries( - description.SystemDescription, - description.Location, - description.WithAnyTypes, - lambdaParameters, - "WithAny", - "lambda parameter", - compareTypeSymbolsOnly: true); - - var containingSystemTypeSymbol = (INamedTypeSymbol)description.SystemDescription.SemanticModel.GetDeclaredSymbol(description.SystemDescription.SystemTypeSyntax); - if (containingSystemTypeSymbol.GetMembers().OfType().Any(method => method.Name == "OnCreateForCompiler")) - { - LambdaJobsErrors.DC0025(description.SystemDescription, description.Location, containingSystemTypeSymbol.Name); - description.Success = false; - } - - if (containingSystemTypeSymbol.TypeParameters.Any()) - { - LambdaJobsErrors.DC0053(description.SystemDescription, description.Location, containingSystemTypeSymbol.Name, description.LambdaJobKind); - description.Success = false; - } + description.Success &= + QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithAllTypes, invokedMethodName: "WithAll"); + description.Success &= + QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithNoneTypes, invokedMethodName: "WithNone"); + description.Success &= + QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithAnyTypes, invokedMethodName: "WithAny"); + description.Success &= + QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithDisabledTypes, invokedMethodName: "WithDisabled"); + description.Success &= + QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithAbsentTypes, invokedMethodName: "WithAbsent"); + description.Success &= + QueryVerification.VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.WithPresentTypes, invokedMethodName: "WithPresent"); + + description.Success &= + QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithAllTypes); + description.Success &= + QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithAnyTypes); + description.Success &= + QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithNoneTypes); + description.Success &= + QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithDisabledTypes); + description.Success &= + QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithAbsentTypes); + description.Success &= + QueryVerification.VerifySharedComponentFilterTypesAgainstOtherQueryTypes(description.SystemDescription, description.Location, description.WithSharedComponentFilterTypes, description.WithPresentTypes); + + description.Success &= + QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithAbsentTypes, description.WithPresentTypes, "WithAbsent", "WithPresent"); + description.Success &= + QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithNoneTypes, description.WithPresentTypes, "WithNone", "WithPresent"); + description.Success &= + QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithAnyTypes, description.WithPresentTypes, "WithAny", "WithPresent"); + description.Success &= + QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithAbsentTypes, description.WithDisabledTypes, "WithAbsent", "WithDisabled"); + description.Success &= + QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithAbsentTypes, description.WithAllTypes, "WithAbsent", "WithAll"); + description.Success &= + QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithAbsentTypes, description.WithAnyTypes, "WithAbsent", "WithAny"); + description.Success &= + QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithNoneTypes, description.WithAllTypes, "WithNone", "WithAll"); + description.Success &= + QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithNoneTypes, description.WithAnyTypes, "WithNone", "WithAny"); + description.Success &= + QueryVerification.VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.WithAnyTypes, description.WithAllTypes, "WithAny", "WithAll"); + + var lambdaParameters = + description.LambdaParameters + .Where(param => param.IsQueryableType) + .Select(param => + new Query + { + TypeSymbol = param.TypeSymbol, + Type = QueryType.All, + IsReadOnly = param.QueryTypeIsReadOnly() + }) + .ToArray(); + + description.Success &= QueryVerification.VerifyNoMutuallyExclusiveQueries( + description.SystemDescription, + description.Location, + description.WithNoneTypes, + lambdaParameters, + "WithNone", + "lambda parameter", + compareTypeSymbolsOnly: true); + + description.Success &= QueryVerification.VerifyNoMutuallyExclusiveQueries( + description.SystemDescription, + description.Location, + description.WithAnyTypes, + lambdaParameters, + "WithAny", + "lambda parameter", + compareTypeSymbolsOnly: true); + + var containingSystemTypeSymbol = (INamedTypeSymbol)description.SystemDescription.SemanticModel.GetDeclaredSymbol(description.SystemDescription.SystemTypeSyntax); + if (containingSystemTypeSymbol.GetMembers().OfType().Any(method => method.Name == "OnCreateForCompiler")) + { + LambdaJobsErrors.DC0025(description.SystemDescription, description.Location, containingSystemTypeSymbol.Name); + description.Success = false; + } - var containingMethodSymbol = description.SystemDescription.SemanticModel.GetDeclaredSymbol(description.ContainingMethod); - if (containingMethodSymbol is IMethodSymbol methodSymbol && methodSymbol.TypeArguments.Any()) - { - LambdaJobsErrors.DC0054(description.SystemDescription, description.Location, methodSymbol.Name, description.LambdaJobKind); - description.Success = false; - } + if (containingSystemTypeSymbol.TypeParameters.Any()) + { + LambdaJobsErrors.DC0053(description.SystemDescription, description.Location, containingSystemTypeSymbol.Name, description.LambdaJobKind); + description.Success = false; + } - return success; + var containingMethodSymbol = description.SystemDescription.SemanticModel.GetDeclaredSymbol(description.ContainingMethod); + if (containingMethodSymbol is IMethodSymbol methodSymbol && methodSymbol.TypeArguments.Any()) + { + LambdaJobsErrors.DC0054(description.SystemDescription, description.Location, methodSymbol.Name, description.LambdaJobKind); + description.Success = false; } - static bool VerifyDC0073(LambdaJobDescription description) + return success; + } + + static bool DoesPerformStructuralChange(InvocationExpressionSyntax syntax, SemanticModel model) + { + return model.GetSymbolInfo(syntax.Expression).Symbol is IMethodSymbol methodSymbol && + methodSymbol.ContainingType.Is("Unity.Entities.EntityManager") && + methodSymbol.HasAttribute("Unity.Entities.EntityManager.StructuralChangeMethodAttribute"); + } + + static bool VerifyDC0073(LambdaJobDescription description) + { + if ( description.WithScheduleGranularityArgumentSyntaxes.Count > 0 + && description.Schedule.Mode != ScheduleMode.ScheduleParallel) { - if ( description.WithScheduleGranularityArgumentSyntaxes.Count > 0 - && description.Schedule.Mode != ScheduleMode.ScheduleParallel) - { - LambdaJobsErrors.DC0073(description.SystemDescription, description.Location); - return false; - } - return true; + LambdaJobsErrors.DC0073(description.SystemDescription, description.Location); + return false; } + return true; } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsCandidate.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsCandidate.cs index aefa4b6..41977be 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsCandidate.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsCandidate.cs @@ -4,38 +4,37 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +public enum LambdaJobKind { - public enum LambdaJobKind - { - Job, - Entities - } + Job, + Entities +} - public static class LambdaJobKindExtensions +public static class LambdaJobKindExtensions +{ + public static string ToName(this LambdaJobKind lambdaJobKind) => lambdaJobKind switch { - public static string ToName(this LambdaJobKind lambdaJobKind) => lambdaJobKind switch - { - LambdaJobKind.Job => "Job.WithCode", - LambdaJobKind.Entities => "Entities.ForEach", - _ => throw new ArgumentOutOfRangeException() - }; + LambdaJobKind.Job => "Job.WithCode", + LambdaJobKind.Entities => "Entities.ForEach", + _ => throw new ArgumentOutOfRangeException() + }; - public static string ToNameOfValidAlternativeFeatures(this LambdaJobKind lambdaJobKind, ScheduleMode scheduleMode) => lambdaJobKind switch - { - LambdaJobKind.Job => "IJob", - LambdaJobKind.Entities when scheduleMode == ScheduleMode.Run => "SystemAPI.Query, IJobEntity or IJobChunk", - LambdaJobKind.Entities => "IJobEntity or IJobChunk", - _ => throw new ArgumentOutOfRangeException(nameof(lambdaJobKind), lambdaJobKind, null) - }; - } - - public struct LambdaJobsCandidate : ISystemCandidate + public static string ToNameOfValidAlternativeFeatures(this LambdaJobKind lambdaJobKind, ScheduleMode scheduleMode) => lambdaJobKind switch { - public LambdaJobKind LambdaJobKind { get; set; } - public SyntaxNode Node { get; set; } - public TypeDeclarationSyntax ContainingSystemType { get; set; } - public Dictionary> MethodInvocations { get; set; } - public string CandidateTypeName => LambdaJobKind.ToName(); - } + LambdaJobKind.Job => "IJob", + LambdaJobKind.Entities when scheduleMode == ScheduleMode.Run => "SystemAPI.Query, IJobEntity or IJobChunk", + LambdaJobKind.Entities => "IJobEntity or IJobChunk", + _ => throw new ArgumentOutOfRangeException(nameof(lambdaJobKind), lambdaJobKind, null) + }; +} + +public struct LambdaJobsCandidate : ISystemCandidate +{ + public LambdaJobKind LambdaJobKind { get; set; } + public SyntaxNode Node { get; set; } + public TypeDeclarationSyntax ContainingSystemType { get; set; } + public Dictionary> MethodInvocations { get; set; } + public string CandidateTypeName => LambdaJobKind.ToName(); } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsErrors.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsErrors.cs index 8094db4..694c010 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsErrors.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsErrors.cs @@ -1,316 +1,320 @@ #pragma warning disable 8509 -using System; using Microsoft.CodeAnalysis; using Unity.Entities.SourceGen.Common; using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +public static class LambdaJobsErrors { - public static class LambdaJobsErrors - { - const string k_ErrorTitle = "Lambda Jobs Error"; - - public static void DC0003(SystemDescription systemDescription, Location location, string name) - { - systemDescription.LogError(nameof(DC0003), k_ErrorTitle, - $"The name '{name}' is already used in this system.", location); - } - - public static void DC0004(SystemDescription systemDescription, Location location, string variableName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0004), k_ErrorTitle, - $"{lambdaJobKind.ToName()} Lambda expression captures a non-value type '{variableName}'. " - + "This is either due to use of a field, a member method, or property of the containing system, or the cause of explicit use of the this keyword. ".EmitIfTrue(variableName=="this") - + "This is only allowed with .WithoutBurst() and .Run()", location); - } - - public static void DC0005(SystemDescription systemDescription, Location location, string parameterName, string parameterTypeName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0005), k_ErrorTitle, - $"{lambdaJobKind.ToName()} Lambda expression parameter '{parameterName}' with type {parameterTypeName} is not supported", location); - } - - public static void DC0008(SystemDescription systemDescription, Location location, string methodName) - { - systemDescription.LogError(nameof(DC0008), k_ErrorTitle, - $"The argument to {methodName} needs to be a literal value.", location); - } - - public static void DC0009(SystemDescription systemDescription, Location location, string methodName) - { - systemDescription.LogError(nameof(DC0009), k_ErrorTitle, - $"{methodName} is only allowed to be called once.", location); - } - - public static void DC0010(SystemDescription systemDescription, Location location, string methodName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0010), k_ErrorTitle, - $"The {lambdaJobKind.ToName()} statement contains dynamic code in {methodName} that cannot be statically analyzed.", location); - } - - public static void DC0012(SystemDescription systemDescription, Location location, string argumentName, string constructionMethodName) - { - systemDescription.LogError(nameof(DC0012), k_ErrorTitle, - $"Entities.{constructionMethodName} is called with an invalid argument {argumentName}. You can only use Entities.{constructionMethodName} on local variables that are captured inside the lambda. Please assign the field to a local variable and use that instead.", location); - } - - public static void DC0013(SystemDescription systemDescription, Location location, string capturedVariableName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0013), k_ErrorTitle, - $"{lambdaJobKind.ToName()} Lambda expression writes to captured variable '{capturedVariableName}' that is then read outside. This is only supported when you use .Run().", location); - } - - public static void DC0014(SystemDescription systemDescription, Location location, string parameterName, string[] supportedParameters) - { - systemDescription.LogError(nameof(DC0014), k_ErrorTitle, - $"Execute() parameter '{parameterName}' is not a supported parameter in an IJobEntitiesForEach type. Supported `int` parameter names are {supportedParameters.SeparateByComma()}.", location); - } - - public static void DC0223(SystemDescription systemDescription, Location location, string typeName, bool isManagedIComponentData, LambdaJobKind lambdaJobKind) - { - var componentText = isManagedIComponentData ? $"managed IComponentData `{typeName}`" : $"ISharedComponentData type {typeName}"; - var message = $"{lambdaJobKind.ToName()} uses {componentText}. This is only supported when using .WithoutBurst() and .Run()."; - systemDescription.LogError(nameof(DC0223), k_ErrorTitle, message, location); - } - - public static void DC0020(SystemDescription systemDescription, Location location, string sharedComponentTypeName) - { - systemDescription.LogError(nameof(DC0020), k_ErrorTitle, - $"ISharedComponentData type {sharedComponentTypeName} can not be received by ref. Use by value or in.", location); - } - - public static void DC0021(SystemDescription systemDescription, Location location, string parameterName, string unsupportedTypeName) - { - systemDescription.LogError(nameof(DC0021), k_ErrorTitle, - $"parameter '{parameterName}' has type {unsupportedTypeName}. This type is not a IComponentData / ISharedComponentData and is therefore not a supported parameter type for Entities.ForEach.", location); - } - - public static void DC0024(SystemDescription systemDescription, Location location, string componentTypeName) - { - systemDescription.LogError(nameof(DC0024), k_ErrorTitle, - $"Entities.ForEach uses managed IComponentData `{componentTypeName}` by ref. To get write access, receive it without the ref modifier.", location); - } - - public static void DC0025(SystemDescription systemDescription, Location location, string typeName) - { - systemDescription.LogError(nameof(DC0025), k_ErrorTitle, - $"Type `{typeName}` is not allowed to implement method `OnCreateForCompiler'. This method is supplied by code generation.", location); - } - - public static void DC0027(SystemDescription systemDescription, Location location, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0027), k_ErrorTitle, - $"{lambdaJobKind.ToName()} Lambda expression makes a structural change. Use an EntityCommandBuffer to make structural changes or add a .WithStructuralChanges invocation to the {lambdaJobKind.ToName()} to allow for structural changes. Note: WithStructuralChanges is runs without burst and is only allowed with .Run().", location); - } - - public static void DC0029(SystemDescription systemDescription, Location location, LambdaJobKind lambdaJobKind) - { - var lambdaJobKindName = lambdaJobKind.ToName(); - systemDescription.LogError(nameof(DC0029), k_ErrorTitle, $"{lambdaJobKindName} Lambda expression has a nested {lambdaJobKindName} Lambda expression. Only a single {lambdaJobKindName} Lambda expression is currently supported.", location); - } - - public static void DC0031(SystemDescription systemDescription, Location location) - { - systemDescription.LogError(nameof(DC0031), k_ErrorTitle, - $"Entities.ForEach Lambda expression stores the EntityQuery with a .WithStoreEntityQueryInField invocation but does not store it in a valid field. Entity Queries can only be stored in fields of the containing SystemBase.", location); - } - - public static void DC0033(SystemDescription systemDescription, Location location, string parameterName, string unsupportedTypeName) - { - systemDescription.LogError(nameof(DC0033), k_ErrorTitle, - $"{unsupportedTypeName} implements IBufferElementData and must be used as DynamicBuffer<{unsupportedTypeName}>. Parameter '{parameterName}' is not a IComponentData / ISharedComponentData and is therefore not a supported parameter type for Entities.ForEach.", location); - } - - public static void DC0034(SystemDescription systemDescription, Location location, string argumentName, string unsupportedTypeName, string constructionMethodName) - { - systemDescription.LogError(nameof(DC0034), k_ErrorTitle, - $"Entities.{constructionMethodName} is called with an argument {argumentName} of unsupported type {unsupportedTypeName}. It can only be called with an argument that is marked with [NativeContainerAttribute] or a type that has a field marked with [NativeContainerAttribute].", location); - } - - public static void DC0035(SystemDescription systemDescription, Location location, string argumentName, string constructionMethodName) - { - systemDescription.LogError(nameof(DC0035), k_ErrorTitle, - $"Entities.{constructionMethodName} is called with argument {argumentName}, but that value is not used in the lambda function.", location); - } - - public static void DC0043(SystemDescription systemDescription, Location location, string jobName) - { - systemDescription.LogError(nameof(DC0043), k_ErrorTitle, - $"Entities.WithName cannot be used with name '{jobName}'. The given name must consist of letters, digits, and underscores only, and may not contain two consecutive underscores.", location); - } - - public static void DC0044(SystemDescription systemDescription, Location location, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0044), k_ErrorTitle, - $"{lambdaJobKind.ToName()} can only be used with an inline lambda. Calling it with a delegate stored in a variable, field, or returned from a method is not supported.", location); - } - - public static void DC0046(SystemDescription systemDescription, Location location, string methodName, string typeName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0046), k_ErrorTitle, - $"{lambdaJobKind.ToName()} cannot use component access method {methodName} that needs write access with the same type {typeName} that is used in lambda parameters.", location); - } - - public static void DC0047(SystemDescription systemDescription, Location location, string methodName, string typeName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0047), k_ErrorTitle, - $"{lambdaJobKind.ToName()} cannot use component access method {methodName} with the same type {typeName} that is used in lambda parameters with write access (as ref).", location); - } - - public static void DC0050(SystemDescription systemDescription, Location location, string parameterTypeFullName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0050), k_ErrorTitle, - $"Type {parameterTypeFullName} cannot be used as an {lambdaJobKind.ToName()} parameter as generic types and generic parameters are not currently supported in {lambdaJobKind.ToName()}", location); - } - - public static void DC0053(SystemDescription systemDescription, Location location, string systemTypeName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0053), k_ErrorTitle, - $"{lambdaJobKind.ToName()} cannot be used in system {systemTypeName} as {lambdaJobKind.ToName()} in generic system types are not supported.", location); - } - - public static void DC0054(SystemDescription systemDescription, Location location, string methodName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0054), k_ErrorTitle, - $"{lambdaJobKind.ToName()} is used in generic method {methodName}. This is not currently supported.", location); - } - - public static void DC0055(SystemDescription systemDescription, Location location, string lambdaParameterComponentTypeName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogWarning(nameof(DC0055), k_ErrorTitle, - $"{lambdaJobKind.ToName()} passes {lambdaParameterComponentTypeName} by value. Any changes made will not be stored to the underlying component. Please specify the access you require. Use 'in' for read-only access or `ref` for read-write access.", location); - } - - public static void DC0057(SystemDescription systemDescription, Location location) - { - systemDescription.LogError(nameof(DC0057), k_ErrorTitle, - "WithStructuralChanges cannot be used with Job.WithCode. WithStructuralChanges should instead be used with Entities.ForEach.", location); - } - - public static void DC0059(SystemDescription systemDescription, Location location, string methodName, LambdaJobKind lambdaJobKind) - { - systemDescription.LogError(nameof(DC0059), k_ErrorTitle, - $"The argument indicating read only access to {methodName} cannot be dynamic and must to be a boolean literal `true` or `false` value when used inside of an {lambdaJobKind.ToName()}.", location); - } - - public static void DC0070(SystemDescription systemDescription, Location location, ITypeSymbol duplicateType) - { - systemDescription.LogError( - nameof(DC0070), k_ErrorTitle, - $"{duplicateType.Name} is used multiple times as a lambda parameter. Each IComponentData, ISharedComponentData, DynamicBuffer type may only be used once in Entities.ForEach().", location); - } - - public static void DC0073(SystemDescription systemDescription, Location location) - { - systemDescription.LogError(nameof(DC0073), - k_ErrorTitle, - "WithScheduleGranularity cannot be used with Schedule or Run as it controls how parallel job scheduling occurs. Use ScheduleParallel with this feature.", location); - } - - public static void DC0074(SystemDescription systemDescription, Location location) - { - systemDescription.LogError( - nameof(DC0074), - k_ErrorTitle, - "When using an EntityCommandBuffer parameter to an Entities.ForEach() expression, you must do exactly one of the following: " + - "1. Use .WithDeferredPlaybackSystem() with .Run(), .Schedule() or .ScheduleParallel() to play back entity commands when the EntityCommandBufferSystem runs. " + - "2. Use .WithImmediatePlayback() with .Run() to play commands back immediately after this Entities.ForEach() runs. ", - location); - } - - public static void DC0075(SystemDescription systemDescription, Location location) - { - systemDescription.LogError( - nameof(DC0075), - k_ErrorTitle, - "You have invoked both .WithDeferredPlaybackSystem() and .WithImmediatePlayback() in the same Entities.ForEach() expression. " + - "Allowed usages are: " + - "1. If you are using an EntityCommandBuffer parameter, specify either .WithDeferredPlaybackSystem() or .WithImmediatePlayback(); " + - "2. If not, use neither.", - location); - } - - public static void DC0076(SystemDescription systemDescription, Location location) - { - systemDescription.LogError( - nameof(DC0076), - k_ErrorTitle, - "You have specified more than one EntityCommandBuffer parameter in the same Entities.ForEach() expression. This is not allowed. " + - "Please specify at most one EntityCommandBuffer parameter in each Entities.ForEach().", - location); - } - - public static void DC0077(SystemDescription systemDescription, Location location) - { - systemDescription.LogError( - nameof(DC0077), - k_ErrorTitle, - "You invoked .WithImmediatePlayback() together with .Schedule()/.ScheduleParallel(). This is not allowed. " + - ".WithImmediatePlayback() may only be used with .Run().", - location); - } - - public static void DC0078(SystemDescription systemDescription, Location location) - { - systemDescription.LogError( - nameof(DC0078), - k_ErrorTitle, - "You invoked .WithDeferredPlaybackSystem() multiple times. Please invoke it EXACTLY once to specify EXACTLY one system for playing back commands.", - location); - } - - public static void DC0079(SystemDescription systemDescription, string methodSignature, Location location) - { - systemDescription.LogError( - nameof(DC0079), - k_ErrorTitle, - "The only EntityCommandBuffer methods you may use inside of Entities.ForEach() are those which are marked with the [SupportedInEntitiesForEach] attribute. " + - $"You attempted to invoke {methodSignature}, whose usage is not supported inside of Entities.ForEach().", - location); - } - - public static void DC0080(SystemDescription systemDescription, string methodSignature, Location location) - { - systemDescription.LogError( - nameof(DC0080), - k_ErrorTitle, - "The following EntityCommandBuffer methods are supported ONLY on the main thread: 1) Those with an EntityQuery parameter, and 2) those accepting managed IComponentData or IEnableableComponent types. " + - $"This means you may only use {methodSignature} inside of Entities.ForEach() when calling Run().", - location); - } - - public static void DC0081(SystemDescription systemDescription, string parallelWriterArgumentName, Location location) - { - systemDescription.LogError( - nameof(DC0081), - k_ErrorTitle, - "You are not allowed to pass a Unity.Entities.EntityCommandBuffer.ParallelWriter parameter to the Entities.ForEach() lambda function. " + - "Instead, please do the following: Pass an Unity.Entities.EntityCommandBuffer parameter, specify an EntityCommandBufferSystem by calling .WithDeferredPlaybackSystem(), " + - "and finally invoke .ScheduleParallel(). A parallel writer instance will automatically be created and run in said system.", - location); - } - public static void DC0082(SystemDescription systemDescription, Location location, string parameterName) - { - systemDescription.LogError(nameof(DC0082), k_ErrorTitle, - $"{parameterName} is an passed with a `ref` or `in` keyword. Aspects are already act as reference types and should just be passed in by value.", location); - } - - public static void DC0083(SystemDescription systemDescription, Location location, LambdaJobKind kind, ScheduleMode scheduleMode) - { - systemDescription.LogError(nameof(DC0083), k_ErrorTitle, - $"Capturing local functions are not allowed in {kind.ToName()}. Consider using {kind.ToNameOfValidAlternativeFeatures(scheduleMode)} instead.", location); - } - - public static void DC0084(SystemDescription systemDescription, Location location, LambdaJobKind kind, ScheduleMode scheduleMode) - { - systemDescription.LogError(nameof(DC0084), k_ErrorTitle, - $"Anonymous functions are not allowed in {kind.ToName()}. Consider using {kind.ToNameOfValidAlternativeFeatures(scheduleMode)} instead, without burst.", location); - } - - public static void DC0085(SystemDescription systemDescription, Location location, LambdaJobKind kind, ScheduleMode scheduleMode) - { - systemDescription.LogError(nameof(DC0085), k_ErrorTitle, - $"Defining local functions are not allowed in {kind.ToName()}. Consider using {kind.ToNameOfValidAlternativeFeatures(scheduleMode)} instead.", location); - } + const string k_ErrorTitle = "Lambda Jobs Error"; + + public static void DC0003(SystemDescription systemDescription, Location location, string name) + { + systemDescription.LogError(nameof(DC0003), k_ErrorTitle, + $"The name '{name}' is already used in this system.", location); + } + + public static void DC0004(SystemDescription systemDescription, Location location, string variableName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0004), k_ErrorTitle, + $"{lambdaJobKind.ToName()} Lambda expression captures a non-value type '{variableName}'. " + + "This is either due to use of a field, a member method, or property of the containing system, or the cause of explicit use of the this keyword. ".EmitIfTrue(variableName=="this") + + "This is only allowed with .WithoutBurst() and .Run()", location); + } + + public static void DC0005(SystemDescription systemDescription, Location location, string parameterName, string parameterTypeName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0005), k_ErrorTitle, + $"{lambdaJobKind.ToName()} Lambda expression parameter '{parameterName}' with type {parameterTypeName} is not supported", location); + } + + public static void DC0008(SystemDescription systemDescription, Location location, string methodName) + { + systemDescription.LogError(nameof(DC0008), k_ErrorTitle, + $"The argument to {methodName} needs to be a literal value.", location); + } + + public static void DC0009(SystemDescription systemDescription, Location location, string methodName) + { + systemDescription.LogError(nameof(DC0009), k_ErrorTitle, + $"{methodName} is only allowed to be called once.", location); + } + + public static void DC0010(SystemDescription systemDescription, Location location, string methodName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0010), k_ErrorTitle, + $"The {lambdaJobKind.ToName()} statement contains dynamic code in {methodName} that cannot be statically analyzed.", location); + } + + public static void DC0012(SystemDescription systemDescription, Location location, string argumentName, string constructionMethodName) + { + systemDescription.LogError(nameof(DC0012), k_ErrorTitle, + $"Entities.{constructionMethodName} is called with an invalid argument {argumentName}. You can only use Entities.{constructionMethodName} on local variables that are captured inside the lambda. Please assign the field to a local variable and use that instead.", location); + } + + public static void DC0013(SystemDescription systemDescription, Location location, string capturedVariableName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0013), k_ErrorTitle, + $"{lambdaJobKind.ToName()} Lambda expression writes to captured variable '{capturedVariableName}' that is then read outside. This is only supported when you use .Run().", location); + } + + public static void DC0014(SystemDescription systemDescription, Location location, string parameterName, string[] supportedParameters) + { + systemDescription.LogError(nameof(DC0014), k_ErrorTitle, + $"Execute() parameter '{parameterName}' is not a supported parameter in an IJobEntitiesForEach type. Supported `int` parameter names are {supportedParameters.SeparateByComma()}.", location); + } + + public static void DC0223(SystemDescription systemDescription, Location location, string typeName, bool isManagedIComponentData, LambdaJobKind lambdaJobKind) + { + var componentText = isManagedIComponentData ? $"managed IComponentData `{typeName}`" : $"ISharedComponentData type {typeName}"; + var message = $"{lambdaJobKind.ToName()} uses {componentText}. This is only supported when using .WithoutBurst() and .Run()."; + systemDescription.LogError(nameof(DC0223), k_ErrorTitle, message, location); + } + + public static void DC0020(SystemDescription systemDescription, Location location, string sharedComponentTypeName) + { + systemDescription.LogError(nameof(DC0020), k_ErrorTitle, + $"ISharedComponentData type {sharedComponentTypeName} can not be received by ref. Use by value or in.", location); + } + + public static void DC0021(SystemDescription systemDescription, Location location, string parameterName, string unsupportedTypeName) + { + systemDescription.LogError(nameof(DC0021), k_ErrorTitle, + $"parameter '{parameterName}' has type {unsupportedTypeName}. This type is not a IComponentData / ISharedComponentData and is therefore not a supported parameter type for Entities.ForEach.", location); + } + + public static void DC0024(SystemDescription systemDescription, Location location, string componentTypeName) + { + systemDescription.LogError(nameof(DC0024), k_ErrorTitle, + $"Entities.ForEach uses managed IComponentData `{componentTypeName}` by ref. To get write access, receive it without the ref modifier.", location); + } + + public static void DC0025(SystemDescription systemDescription, Location location, string typeName) + { + systemDescription.LogError(nameof(DC0025), k_ErrorTitle, + $"Type `{typeName}` is not allowed to implement method `OnCreateForCompiler'. This method is supplied by code generation.", location); + } + + public static void DC0027(SystemDescription systemDescription, Location location, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0027), k_ErrorTitle, + $"{lambdaJobKind.ToName()} Lambda expression makes a structural change. Use an EntityCommandBuffer to make structural changes or add a .WithStructuralChanges invocation to the {lambdaJobKind.ToName()} to allow for structural changes. Note: WithStructuralChanges is runs without burst and is only allowed with .Run().", location); + } + + public static void DC0029(SystemDescription systemDescription, Location location, LambdaJobKind lambdaJobKind) + { + var lambdaJobKindName = lambdaJobKind.ToName(); + systemDescription.LogError(nameof(DC0029), k_ErrorTitle, $"{lambdaJobKindName} Lambda expression has a nested {lambdaJobKindName} Lambda expression. Only a single {lambdaJobKindName} Lambda expression is currently supported.", location); + } + + public static void DC0031(SystemDescription systemDescription, Location location) + { + systemDescription.LogError(nameof(DC0031), k_ErrorTitle, + $"Entities.ForEach Lambda expression stores the EntityQuery with a .WithStoreEntityQueryInField invocation but does not store it in a valid field. Entity Queries can only be stored in fields of the containing SystemBase.", location); + } + + public static void DC0033(SystemDescription systemDescription, Location location, string parameterName, string unsupportedTypeName) + { + systemDescription.LogError(nameof(DC0033), k_ErrorTitle, + $"{unsupportedTypeName} implements IBufferElementData and must be used as DynamicBuffer<{unsupportedTypeName}>. Parameter '{parameterName}' is not a IComponentData / ISharedComponentData and is therefore not a supported parameter type for Entities.ForEach.", location); + } + + public static void DC0034(SystemDescription systemDescription, Location location, string argumentName, string unsupportedTypeName, string constructionMethodName) + { + systemDescription.LogError(nameof(DC0034), k_ErrorTitle, + $"Entities.{constructionMethodName} is called with an argument {argumentName} of unsupported type {unsupportedTypeName}. It can only be called with an argument that is marked with [NativeContainerAttribute] or a type that has a field marked with [NativeContainerAttribute].", location); + } + + public static void DC0035(SystemDescription systemDescription, Location location, string argumentName, string constructionMethodName) + { + systemDescription.LogError(nameof(DC0035), k_ErrorTitle, + $"Entities.{constructionMethodName} is called with argument {argumentName}, but that value is not used in the lambda function.", location); + } + + public static void DC0043(SystemDescription systemDescription, Location location, string jobName) + { + systemDescription.LogError(nameof(DC0043), k_ErrorTitle, + $"Entities.WithName cannot be used with name '{jobName}'. The given name must consist of letters, digits, and underscores only, and may not contain two consecutive underscores.", location); + } + + public static void DC0044(SystemDescription systemDescription, Location location, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0044), k_ErrorTitle, + $"{lambdaJobKind.ToName()} can only be used with an inline lambda. Calling it with a delegate stored in a variable, field, or returned from a method is not supported.", location); + } + + public static void DC0046(SystemDescription systemDescription, Location location, string methodName, string typeName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0046), k_ErrorTitle, + $"{lambdaJobKind.ToName()} cannot use component access method {methodName} that needs write access with the same type {typeName} that is used in lambda parameters.", location); + } + + public static void DC0047(SystemDescription systemDescription, Location location, string methodName, string typeName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0047), k_ErrorTitle, + $"{lambdaJobKind.ToName()} cannot use component access method {methodName} with the same type {typeName} that is used in lambda parameters with write access (as ref).", location); + } + + public static void DC0050(SystemDescription systemDescription, Location location, string parameterTypeFullName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0050), k_ErrorTitle, + $"Type {parameterTypeFullName} cannot be used as an {lambdaJobKind.ToName()} parameter as generic types and generic parameters are not currently supported in {lambdaJobKind.ToName()}", location); + } + + public static void DC0053(SystemDescription systemDescription, Location location, string systemTypeName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0053), k_ErrorTitle, + $"{lambdaJobKind.ToName()} cannot be used in system {systemTypeName} as {lambdaJobKind.ToName()} in generic system types are not supported.", location); + } + + public static void DC0054(SystemDescription systemDescription, Location location, string methodName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0054), k_ErrorTitle, + $"{lambdaJobKind.ToName()} is used in generic method {methodName}. This is not currently supported.", location); + } + + public static void DC0055(SystemDescription systemDescription, Location location, string lambdaParameterComponentTypeName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogWarning(nameof(DC0055), k_ErrorTitle, + $"{lambdaJobKind.ToName()} passes {lambdaParameterComponentTypeName} by value. Any changes made will not be stored to the underlying component. Please specify the access you require. Use 'in' for read-only access or `ref` for read-write access.", location); + } + + public static void DC0057(SystemDescription systemDescription, Location location) + { + systemDescription.LogError(nameof(DC0057), k_ErrorTitle, + "WithStructuralChanges cannot be used with Job.WithCode. WithStructuralChanges should instead be used with Entities.ForEach.", location); + } + + public static void DC0059(SystemDescription systemDescription, Location location, string methodName, LambdaJobKind lambdaJobKind) + { + systemDescription.LogError(nameof(DC0059), k_ErrorTitle, + $"The argument indicating read only access to {methodName} cannot be dynamic and must to be a boolean literal `true` or `false` value when used inside of an {lambdaJobKind.ToName()}.", location); + } + + public static void DC0070(SystemDescription systemDescription, Location location, ITypeSymbol duplicateType) + { + systemDescription.LogError( + nameof(DC0070), k_ErrorTitle, + $"{duplicateType.Name} is used multiple times as a lambda parameter. Each IComponentData, ISharedComponentData, DynamicBuffer type may only be used once in Entities.ForEach().", location); + } + + public static void DC0073(SystemDescription systemDescription, Location location) + { + systemDescription.LogError(nameof(DC0073), + k_ErrorTitle, + "WithScheduleGranularity cannot be used with Schedule or Run as it controls how parallel job scheduling occurs. Use ScheduleParallel with this feature.", location); + } + + public static void DC0074(SystemDescription systemDescription, Location location) + { + systemDescription.LogError( + nameof(DC0074), + k_ErrorTitle, + "When using an EntityCommandBuffer parameter to an Entities.ForEach() expression, you must do exactly one of the following: " + + "1. Use .WithDeferredPlaybackSystem() with .Run(), .Schedule() or .ScheduleParallel() to play back entity commands when the EntityCommandBufferSystem runs. " + + "2. Use .WithImmediatePlayback() with .Run() to play commands back immediately after this Entities.ForEach() runs. ", + location); + } + + public static void DC0075(SystemDescription systemDescription, Location location) + { + systemDescription.LogError( + nameof(DC0075), + k_ErrorTitle, + "You have invoked both .WithDeferredPlaybackSystem() and .WithImmediatePlayback() in the same Entities.ForEach() expression. " + + "Allowed usages are: " + + "1. If you are using an EntityCommandBuffer parameter, specify either .WithDeferredPlaybackSystem() or .WithImmediatePlayback(); " + + "2. If not, use neither.", + location); + } + + public static void DC0076(SystemDescription systemDescription, Location location) + { + systemDescription.LogError( + nameof(DC0076), + k_ErrorTitle, + "You have specified more than one EntityCommandBuffer parameter in the same Entities.ForEach() expression. This is not allowed. " + + "Please specify at most one EntityCommandBuffer parameter in each Entities.ForEach().", + location); + } + + public static void DC0077(SystemDescription systemDescription, Location location) + { + systemDescription.LogError( + nameof(DC0077), + k_ErrorTitle, + "You invoked .WithImmediatePlayback() together with .Schedule()/.ScheduleParallel(). This is not allowed. " + + ".WithImmediatePlayback() may only be used with .Run().", + location); + } + + public static void DC0078(SystemDescription systemDescription, Location location) + { + systemDescription.LogError( + nameof(DC0078), + k_ErrorTitle, + "You invoked .WithDeferredPlaybackSystem() multiple times. Please invoke it EXACTLY once to specify EXACTLY one system for playing back commands.", + location); + } + + public static void DC0079(SystemDescription systemDescription, string methodSignature, Location location) + { + systemDescription.LogError( + nameof(DC0079), + k_ErrorTitle, + "The only EntityCommandBuffer methods you may use inside of Entities.ForEach() are those which are marked with the [SupportedInEntitiesForEach] attribute. " + + $"You attempted to invoke {methodSignature}, whose usage is not supported inside of Entities.ForEach().", + location); + } + + public static void DC0080(SystemDescription systemDescription, string methodSignature, Location location) + { + systemDescription.LogError( + nameof(DC0080), + k_ErrorTitle, + "The following EntityCommandBuffer methods are supported ONLY on the main thread: 1) Those with an EntityQuery parameter, and 2) those accepting managed IComponentData or IEnableableComponent types. " + + $"This means you may only use {methodSignature} inside of Entities.ForEach() when calling Run().", + location); + } + + public static void DC0081(SystemDescription systemDescription, string parallelWriterArgumentName, Location location) + { + systemDescription.LogError( + nameof(DC0081), + k_ErrorTitle, + "You are not allowed to pass a Unity.Entities.EntityCommandBuffer.ParallelWriter parameter to the Entities.ForEach() lambda function. " + + "Instead, please do the following: Pass an Unity.Entities.EntityCommandBuffer parameter, specify an EntityCommandBufferSystem by calling .WithDeferredPlaybackSystem(), " + + "and finally invoke .ScheduleParallel(). A parallel writer instance will automatically be created and run in said system.", + location); + } + public static void DC0082(SystemDescription systemDescription, Location location, string parameterName) + { + systemDescription.LogError(nameof(DC0082), k_ErrorTitle, + $"{parameterName} is an passed with a `ref` or `in` keyword. Aspects are already act as reference types and should just be passed in by value.", location); + } + + public static void DC0083(SystemDescription systemDescription, Location location, LambdaJobKind kind, ScheduleMode scheduleMode) + { + systemDescription.LogError(nameof(DC0083), k_ErrorTitle, + $"Capturing local functions are not allowed in {kind.ToName()}. Consider using {kind.ToNameOfValidAlternativeFeatures(scheduleMode)} instead.", location); + } + + public static void DC0084(SystemDescription systemDescription, Location location, LambdaJobKind kind, ScheduleMode scheduleMode) + { + systemDescription.LogError(nameof(DC0084), k_ErrorTitle, + $"Anonymous functions are not allowed in {kind.ToName()}. Consider using {kind.ToNameOfValidAlternativeFeatures(scheduleMode)} instead, without burst.", location); + } + + public static void DC0085(SystemDescription systemDescription, Location location, LambdaJobKind kind, ScheduleMode scheduleMode) + { + systemDescription.LogError(nameof(DC0085), k_ErrorTitle, + $"Defining local functions are not allowed in {kind.ToName()}. Consider using {kind.ToNameOfValidAlternativeFeatures(scheduleMode)} instead.", location); + } + + public static void DC0086(SystemDescription systemDescription, Location location, IErrorTypeSymbol errorTypeSymbol) + { + systemDescription.LogError(nameof(DC0086), k_ErrorTitle, + $"Failed to load the type of a captured variable. This might be caused by trying to capture a type relying on source-generation. The following candidate reason is given: {errorTypeSymbol.CandidateReason}.", location); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsModule.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsModule.cs index 7c19703..6a6aa42 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsModule.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsModule.cs @@ -1,228 +1,272 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +public class LambdaJobsModule : ISystemModule { - public class LambdaJobsModule : ISystemModule - { - public bool RequiresReferenceToBurst { get; private set; } + public bool RequiresReferenceToBurst { get; private set; } - public IEnumerable<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)> Candidates + public IEnumerable<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)> Candidates + { + get { - get - { - var allCandidates = new List<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)>(); - allCandidates.AddRange(EntitiesForEachCandidates.Select(candidate => - (SyntaxNode: candidate.Node, candidate.ContainingSystemType))); - allCandidates.AddRange(JobWithCodeCandidates.Select(candidate => - (SyntaxNode: candidate.Node, candidate.ContainingSystemType))); - return allCandidates; - } + var allCandidates = new List<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)>(); + allCandidates.AddRange(EntitiesForEachCandidates.Select(candidate => + (SyntaxNode: candidate.Node, candidate.ContainingSystemType))); + allCandidates.AddRange(JobWithCodeCandidates.Select(candidate => + (SyntaxNode: candidate.Node, candidate.ContainingSystemType))); + return allCandidates; } + } - List EntitiesForEachCandidates { get; } = new List(); - List JobWithCodeCandidates { get; } = new List(); + List EntitiesForEachCandidates { get; } = new List(); + List JobWithCodeCandidates { get; } = new List(); - public void OnReceiveSyntaxNode(SyntaxNode node) + public void OnReceiveSyntaxNode(SyntaxNode node, Dictionary candidateOwnership) + { + switch (node) { - switch (node) + case IdentifierNameSyntax identifierNameSyntax + when identifierNameSyntax.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression): { - case IdentifierNameSyntax identifierNameSyntax - when identifierNameSyntax.Parent.IsKind(SyntaxKind.SimpleMemberAccessExpression): + if (identifierNameSyntax.Identifier.Text != "Entities" && + identifierNameSyntax.Identifier.Text != "Job") + break; + + var methodInvocations = GetMethodInvocations(node); + + var jobKind = identifierNameSyntax.Identifier.Text switch { - if (identifierNameSyntax.Identifier.Text != "Entities" && - identifierNameSyntax.Identifier.Text != "Job") - break; + "Entities" => LambdaJobKind.Entities, + "Job" => LambdaJobKind.Job, + _ => throw new ArgumentOutOfRangeException() + }; - var methodInvocations = node.GetMethodInvocations(); + var methodInvocationName = jobKind switch + { + LambdaJobKind.Entities => "ForEach", + LambdaJobKind.Job => "WithCode", + _ => throw new ArgumentOutOfRangeException() + }; - var jobKind = identifierNameSyntax.Identifier.Text switch - { - "Entities" => LambdaJobKind.Entities, - "Job" => LambdaJobKind.Job, - _ => throw new ArgumentOutOfRangeException() - }; + if (methodInvocations.TryGetValue(methodInvocationName, out var invocation)) + { + var isNestedInvocation = invocation.Count > 1; + if (isNestedInvocation) + break; - var methodInvocationName = jobKind switch + var candidateList = jobKind switch { - LambdaJobKind.Entities => "ForEach", - LambdaJobKind.Job => "WithCode", + LambdaJobKind.Entities => EntitiesForEachCandidates, + LambdaJobKind.Job => JobWithCodeCandidates, _ => throw new ArgumentOutOfRangeException() }; - if (methodInvocations.ContainsKey(methodInvocationName)) - { - var isNestedInvocation = methodInvocations[methodInvocationName].Count > 1; - if (isNestedInvocation) break; - - var candidateList = jobKind switch + candidateList.Add( + new LambdaJobsCandidate { - LambdaJobKind.Entities => EntitiesForEachCandidates, - LambdaJobKind.Job => JobWithCodeCandidates, - _ => throw new ArgumentOutOfRangeException() - }; - - candidateList.Add( - new LambdaJobsCandidate - { - Node = node, - ContainingSystemType = node.AncestorOfKind(), - MethodInvocations = methodInvocations, - LambdaJobKind = jobKind - }); - } - - break; + Node = node, + ContainingSystemType = node.AncestorOfKind(), + MethodInvocations = methodInvocations, + LambdaJobKind = jobKind + }); } + break; } } + } - public bool RegisterChangesInSystem(SystemDescription systemDescription) - { - var lambdaJobDescriptions = new List(); + public bool RegisterChangesInSystem(SystemDescription systemDescription) + { + var lambdaJobDescriptions = new List(); - var candidatesGroupedBySystemType = - EntitiesForEachCandidates - .Concat(JobWithCodeCandidates) - .GroupBy(candidate => candidate.ContainingSystemType) - .ToDictionary(g => g.Key, g => g.ToArray()); + var candidatesGroupedBySystemType = + EntitiesForEachCandidates + .Concat(JobWithCodeCandidates) + .GroupBy(candidate => candidate.ContainingSystemType) + .ToDictionary(g => g.Key, g => g.ToArray()); - foreach (var lambdaJobCandidate in candidatesGroupedBySystemType[systemDescription.SystemTypeSyntax]) - { - var lambdaJobDescription = - new LambdaJobDescription(systemDescription, lambdaJobCandidate, - lambdaJobCandidate.Node.AncestorOfKind(), - lambdaJobDescriptions.Count); + foreach (var lambdaJobCandidate in candidatesGroupedBySystemType[systemDescription.SystemTypeSyntax]) + { + var lambdaJobDescription = + new LambdaJobDescription(systemDescription, lambdaJobCandidate, + lambdaJobCandidate.Node.AncestorOfKind(), + lambdaJobDescriptions.Count); - if (lambdaJobDescription.Burst.IsEnabled) - RequiresReferenceToBurst = true; + if (lambdaJobDescription.Burst.IsEnabled) + RequiresReferenceToBurst = true; - if (lambdaJobDescription.Success) lambdaJobDescriptions.Add(lambdaJobDescription); - } + if (lambdaJobDescription.Success) + lambdaJobDescriptions.Add(lambdaJobDescription); + } - // Check to make sure we have no systems with duplicate names - var descriptionsWithDuplicateNames = - lambdaJobDescriptions.GroupBy(desc => desc.Name).Where(g => g.Count() > 1); + // Check to make sure we have no systems with duplicate names + var descriptionsWithDuplicateNames = + lambdaJobDescriptions.GroupBy(desc => desc.Name) + .Where(g => g.Count() > 1); - foreach (var descriptionWithDuplicateName in descriptionsWithDuplicateNames) - { - LambdaJobsErrors.DC0003(systemDescription, descriptionWithDuplicateName.First().Location, descriptionWithDuplicateName.First().Name); - return false; - } + foreach (var descriptionWithDuplicateName in descriptionsWithDuplicateNames) + { + LambdaJobsErrors.DC0003(systemDescription, descriptionWithDuplicateName.First().Location, descriptionWithDuplicateName.First().Name); + return false; + } - foreach (var lambdaJobDescription in lambdaJobDescriptions) + var schedulingInvocationStringBuilder = new StringBuilder(); + foreach (var lambdaJobDescription in lambdaJobDescriptions) + { + if (lambdaJobDescription.LambdaJobKind == LambdaJobKind.Entities) { - if (lambdaJobDescription.LambdaJobKind == LambdaJobKind.Entities) - { - lambdaJobDescription.EntityQueryFieldName = - systemDescription.HandlesDescription.GetOrCreateQueryField( - new SingleArchetypeQueryFieldDescription( - new Archetype( - lambdaJobDescription.LambdaParameters - .Where(param => param is IParamDescription && param.IsQueryableType) - .Select(param => - new Query - { - TypeSymbol = param.EntityQueryTypeSymbol, - Type = QueryType.All, - IsReadOnly = param.QueryTypeIsReadOnly() - }) - .Concat(lambdaJobDescription.WithAllTypes) - .Concat(lambdaJobDescription.WithSharedComponentFilterTypes) - .ToArray(), - lambdaJobDescription.WithAnyTypes, - lambdaJobDescription.WithNoneTypes, - lambdaJobDescription.WithDisabledTypes, - lambdaJobDescription.WithAbsentTypes, - lambdaJobDescription.EntityQueryOptions), - lambdaJobDescription.WithChangeFilterTypes, - queryStorageFieldName: lambdaJobDescription.WithStoreEntityQueryInFieldArgumentSyntaxes.FirstOrDefault()?.Expression.ToString()) - ); - - foreach (var lambdaParameter in lambdaJobDescription.LambdaParameters.OfType()) - { - lambdaParameter.FieldName = lambdaParameter switch - { - LambdaParamDescription_Entity _ => systemDescription.HandlesDescription.GetOrCreateEntityTypeHandleField(), - LambdaParamDescription_DynamicBuffer dynamicBuffer => systemDescription.HandlesDescription.GetOrCreateTypeHandleField(dynamicBuffer.EntityQueryTypeSymbol, lambdaParameter.IsReadOnly), - _ => systemDescription.HandlesDescription.GetOrCreateTypeHandleField(lambdaParameter.TypeSymbol, lambdaParameter.IsReadOnly) - }; - } - - if (lambdaJobDescription.EntityCommandBufferParameter is { Playback: { SystemType: { } } }) - lambdaJobDescription.EntityCommandBufferParameter.GeneratedEcbFieldNameInSystemBaseType = - systemDescription.GetOrCreateEntityCommandBufferSystemField(lambdaJobDescription.EntityCommandBufferParameter.Playback.SystemType); - } + lambdaJobDescription.EntityQueryFieldName = + systemDescription.QueriesAndHandles.GetOrCreateQueryField( + new SingleArchetypeQueryFieldDescription( + new Archetype( + lambdaJobDescription.LambdaParameters + .Where(param => param is IParamDescription && param.IsQueryableType) + .Select(param => + new Query + { + TypeSymbol = param.EntityQueryTypeSymbol, + Type = QueryType.All, + IsReadOnly = param.QueryTypeIsReadOnly() + }) + .Concat(lambdaJobDescription.WithAllTypes) + .Concat(lambdaJobDescription.WithSharedComponentFilterTypes) + .ToArray(), + lambdaJobDescription.WithAnyTypes, + lambdaJobDescription.WithNoneTypes, + lambdaJobDescription.WithDisabledTypes, + lambdaJobDescription.WithAbsentTypes, + lambdaJobDescription.WithPresentTypes, + lambdaJobDescription.EntityQueryOptions), + lambdaJobDescription.WithChangeFilterTypes, + queryStorageFieldName: lambdaJobDescription.WithStoreEntityQueryInFieldArgumentSyntaxes.FirstOrDefault()?.Expression.ToString()) + ); - foreach (var lambdaParameter in lambdaJobDescription.AdditionalFields) + foreach (var lambdaParameter in lambdaJobDescription.LambdaParameters.OfType()) { - switch (lambdaParameter.AccessorDataType) + lambdaParameter.FieldName = lambdaParameter switch { - case LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup: - systemDescription.HandlesDescription.GetOrCreateComponentLookupField( - lambdaParameter.Type, - lambdaParameter.IsReadOnly); - break; - case LambdaJobsPatchableMethod.AccessorDataType.BufferLookup: - systemDescription.HandlesDescription.GetOrCreateBufferLookupField( - lambdaParameter.Type, - lambdaParameter.IsReadOnly); - break; - case LambdaJobsPatchableMethod.AccessorDataType.AspectLookup: - systemDescription.HandlesDescription.GetOrCreateAspectLookup( - lambdaParameter.Type, - lambdaParameter.IsReadOnly); - break; - case LambdaJobsPatchableMethod.AccessorDataType.EntityStorageInfoLookup: - systemDescription.HandlesDescription.GetOrCreateEntityStorageInfoLookupField(); - break; - } + LambdaParamDescription_Entity _ => systemDescription.QueriesAndHandles.GetOrCreateEntityTypeHandleField(), + LambdaParamDescription_DynamicBuffer dynamicBuffer => systemDescription.QueriesAndHandles.GetOrCreateTypeHandleField(dynamicBuffer.EntityQueryTypeSymbol, lambdaParameter.IsReadOnly), + _ => systemDescription.QueriesAndHandles.GetOrCreateTypeHandleField(lambdaParameter.TypeSymbol, lambdaParameter.IsReadOnly) + }; } - systemDescription.NewMiscellaneousMembers.Add(EntitiesSourceFactory.LambdaJobs.JobStructFor(lambdaJobDescription)); + if (lambdaJobDescription.EntityCommandBufferParameter is { Playback: { SystemType: { } } }) + lambdaJobDescription.EntityCommandBufferParameter.GeneratedEcbFieldNameInSystemBaseType = + systemDescription.GetOrCreateEntityCommandBufferSystemField(lambdaJobDescription.EntityCommandBufferParameter.Playback.SystemType); + } - if (lambdaJobDescription.NeedsJobFunctionPointers) + foreach (var lambdaParameter in lambdaJobDescription.AdditionalFields) + { + switch (lambdaParameter.AccessorDataType) { - systemDescription.AdditionalStatementsInOnCreateForCompilerMethod.Add( - $"{lambdaJobDescription.JobStructName}.FunctionPtrFieldNoBurst = {lambdaJobDescription.JobStructName}.RunWithoutJobSystem;"); - if (lambdaJobDescription.Burst.IsEnabled) - { - systemDescription.AdditionalStatementsInOnCreateForCompilerMethod.Add( - $"{lambdaJobDescription.JobStructName}.FunctionPtrFieldBurst = Unity.Entities.Internal.InternalCompilerInterface.BurstCompile({lambdaJobDescription.JobStructName}.FunctionPtrFieldNoBurst);"); - } + case LambdaJobsPatchableMethod.AccessorDataType.ComponentLookup: + systemDescription.QueriesAndHandles.GetOrCreateComponentLookupField( + lambdaParameter.Type, + lambdaParameter.IsReadOnly); + break; + case LambdaJobsPatchableMethod.AccessorDataType.BufferLookup: + systemDescription.QueriesAndHandles.GetOrCreateBufferLookupField( + lambdaParameter.Type, + lambdaParameter.IsReadOnly); + break; + case LambdaJobsPatchableMethod.AccessorDataType.AspectLookup: + systemDescription.QueriesAndHandles.GetOrCreateAspectLookup( + lambdaParameter.Type, + lambdaParameter.IsReadOnly); + break; + case LambdaJobsPatchableMethod.AccessorDataType.EntityStorageInfoLookup: + systemDescription.QueriesAndHandles.GetOrCreateEntityStorageInfoLookupField(); + break; } + } + + if (lambdaJobDescription.NeedsJobFunctionPointers) + { + systemDescription.AdditionalStatementsInOnCreateForCompilerMethod.Add( + $"{lambdaJobDescription.JobStructName}.FunctionPtrFieldNoBurst = {lambdaJobDescription.JobStructName}.RunWithoutJobSystem;"); - if (lambdaJobDescription.EntityCommandBufferParameter is { Playback: { IsImmediate: false } }) + if (lambdaJobDescription.Burst.IsEnabled) { systemDescription.AdditionalStatementsInOnCreateForCompilerMethod.Add( - $"{lambdaJobDescription.EntityCommandBufferParameter.GeneratedEcbFieldNameInSystemBaseType} = " + - $"World.GetOrCreateSystemManaged<{lambdaJobDescription.EntityCommandBufferParameter.Playback.SystemType.ToFullName()}>();"); + $"{lambdaJobDescription.JobStructName}.FunctionPtrFieldBurst = Unity.Entities.Internal.InternalCompilerInterface.BurstCompile({lambdaJobDescription.JobStructName}.FunctionPtrFieldNoBurst);"); } + } + + if (lambdaJobDescription.EntityCommandBufferParameter is { Playback: { IsImmediate: false } }) + { + systemDescription.AdditionalStatementsInOnCreateForCompilerMethod.Add( + $"{lambdaJobDescription.EntityCommandBufferParameter.GeneratedEcbFieldNameInSystemBaseType} = " + + $"World.GetOrCreateSystemManaged<{lambdaJobDescription.EntityCommandBufferParameter.Playback.SystemType.ToFullName()}>();"); + } + + var jobStructWriter = new JobStructWriter { LambdaJobDescription = lambdaJobDescription }; + systemDescription.NewMiscellaneousMembers.Add(jobStructWriter); + + var executeMethodWriter = new ExecuteMethodWriter { LambdaJobDescription = lambdaJobDescription }; + systemDescription.NewMiscellaneousMembers.Add(executeMethodWriter); + } + + var originalNodeToReplacementCode = new Dictionary(); + + // Go through all methods containing descriptions and register syntax replacements with SystemGeneratorContext + foreach (var methodDeclarationSyntax in systemDescription.SystemTypeSyntax.DescendantNodes().OfType()) + { + var lambdaJobDescriptionsInMethods = lambdaJobDescriptions + .Where(desc => desc.ContainingMethod == methodDeclarationSyntax).ToArray(); + + if (!lambdaJobDescriptionsInMethods.Any()) + continue; + + // Replace original invocation expressions for scheduling with replacement syntax + foreach (var desc in lambdaJobDescriptionsInMethods) + { + systemDescription.CandidateNodes.Add( + desc.ContainingInvocationExpression, + new CandidateSyntax(CandidateType.EntitiesForEach, CandidateFlags.None, desc.ContainingInvocationExpression)); + + desc.AppendSchedulingInvocationTo(schedulingInvocationStringBuilder); + originalNodeToReplacementCode.Add(desc.ContainingInvocationExpression, schedulingInvocationStringBuilder.ToString()); - systemDescription.NewMiscellaneousMembers.Add( - EntitiesSourceFactory.LambdaJobs.CreateExecuteMethod(lambdaJobDescription)); + schedulingInvocationStringBuilder.Clear(); } + } - // Go through all methods containing descriptions and register syntax replacements with SystemGeneratorContext - foreach (var methodDeclarationSyntax in systemDescription.SystemTypeSyntax.DescendantNodes().OfType()) - { - var lambdaJobDescriptionsInMethods = lambdaJobDescriptions - .Where(desc => desc.ContainingMethod == methodDeclarationSyntax).ToArray(); + if (originalNodeToReplacementCode.Count > 0) + systemDescription.SyntaxWalkers.Add(Module.EntitiesForEach, new EntitiesForEachSyntaxWalker(originalNodeToReplacementCode)); - if (!lambdaJobDescriptionsInMethods.Any()) - continue; + return true; + } - // Replace original invocation expressions for scheduling with replacement syntax - foreach (var lambdaJobDescriptionInMethod in lambdaJobDescriptionsInMethods) - systemDescription.ReplaceNodeNonNested(lambdaJobDescriptionInMethod.ContainingInvocationExpression, EntitiesSourceFactory.Common.SchedulingInvocationFor(lambdaJobDescriptionInMethod)); + // Walk direct ancestors that are MemberAccessExpressionSyntax and InvocationExpressionSyntax and collect invocations + // This collects things like Entities.WithAll().WithNone().Run() without getting additional ancestor invocations. + static Dictionary> GetMethodInvocations(SyntaxNode node) + { + var result = new Dictionary>(); + var parent = node.Parent; + + while (parent is MemberAccessExpressionSyntax memberAccessExpression) + { + parent = parent.Parent; + if (parent is InvocationExpressionSyntax invocationExpression) + { + var memberName = memberAccessExpression.Name.Identifier.ValueText; + result.Add(memberName, invocationExpression); + parent = parent.Parent; } - return true; + else if (!(parent is MemberAccessExpressionSyntax)) + break; } + + return result; } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsPatchableMethod.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsPatchableMethod.cs index be488bb..f96c5b1 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsPatchableMethod.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaJobsPatchableMethod.cs @@ -1,74 +1,74 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; -using System.Linq; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +class LambdaJobsPatchableMethod { - class LambdaJobsPatchableMethod - { - public Func GeneratePatchedReplacementSyntax; - public ComponentAccessRights AccessRights { get; private set; } + public Func GeneratePatchedReplacementSyntax; + public ComponentAccessRights AccessRights { get; private set; } - public enum AccessorDataType - { - ComponentLookup, - BufferLookup, - AspectLookup, - EntityStorageInfoLookup - } + public enum AccessorDataType + { + ComponentLookup, + BufferLookup, + AspectLookup, + EntityStorageInfoLookup + } - public enum ComponentAccessRights - { - ReadOnly, - ReadWrite, - GetFromFirstMethodParam - } + public enum ComponentAccessRights + { + ReadOnly, + ReadWrite, + GetFromFirstMethodParam + } - static string[] GetArgumentsInOrder(InvocationExpressionSyntax originalNode, params string[] namedParameters) - { - var arguments = originalNode.ArgumentList.Arguments; - if (arguments.Count > 1 && arguments.Count != namedParameters.Length) - throw new InvalidOperationException( - $"Must supply named parameters if there is more than one argument: {string.Join(", ", arguments)} {string.Join(", ", namedParameters)}."); + static string[] GetArgumentsInOrder(InvocationExpressionSyntax originalNode, params string[] namedParameters) + { + var arguments = originalNode.ArgumentList.Arguments; + if (arguments.Count > 1 && arguments.Count != namedParameters.Length) + throw new InvalidOperationException( + $"Must supply named parameters if there is more than one argument: {string.Join(", ", arguments)} {string.Join(", ", namedParameters)}."); - var orderedArguments = new string[arguments.Count]; - var argumentName = arguments.Select(arg => arg.NameColon?.Name); - var argumentsAndName = arguments.Zip(argumentName, (arg, name) => (arg, name)); + var orderedArguments = new string[arguments.Count]; + var argumentName = arguments.Select(arg => arg.NameColon?.Name); + var argumentsAndName = arguments.Zip(argumentName, (arg, name) => (arg, name)); - for (var i = 0; i < 2; i++) + for (var i = 0; i < 2; i++) + { + if (i == 0) // First pass, go through named arguments and fill them in in correct placement { - if (i == 0) // First pass, go through named arguments and fill them in in correct placement + foreach (var arg in argumentsAndName.Where(arg => arg.name != null)) { - foreach (var arg in argumentsAndName.Where(arg => arg.name != null)) - { - var foundIdx = Array.FindIndex(namedParameters, checkArg => checkArg == arg.name.ToString()); - if (foundIdx != -1) - orderedArguments[foundIdx] = arg.arg.Expression.ToString(); - else - throw new InvalidOperationException($"Could not find named parameters {arg.name} in list of expected named parameters."); - } + var foundIdx = Array.FindIndex(namedParameters, checkArg => checkArg == arg.name.ToString()); + if (foundIdx != -1) + orderedArguments[foundIdx] = arg.arg.Expression.ToString(); + else + throw new InvalidOperationException($"Could not find named parameters {arg.name} in list of expected named parameters."); } - else // Second pass, fill in the rest of the arguments in first missing spot + } + else // Second pass, fill in the rest of the arguments in first missing spot + { + var idx = 0; + foreach (var arg in argumentsAndName.Where(arg => arg.name == null)) { - var idx = 0; - foreach (var arg in argumentsAndName.Where(arg => arg.name == null)) - { - while (!string.IsNullOrEmpty(orderedArguments[idx])) - idx++; - if (idx >= orderedArguments.Length) - throw new InvalidOperationException($"Could not fit named and unnamed arguments."); - orderedArguments[idx] = arg.arg.Expression.ToString(); - } + while (!string.IsNullOrEmpty(orderedArguments[idx])) + idx++; + if (idx >= orderedArguments.Length) + throw new InvalidOperationException($"Could not fit named and unnamed arguments."); + orderedArguments[idx] = arg.arg.Expression.ToString(); } } - - return orderedArguments; } - internal static readonly Dictionary PatchableMethods = + return orderedArguments; + } + + internal static readonly Dictionary PatchableMethods = new Dictionary { { @@ -212,5 +212,4 @@ static string[] GetArgumentsInOrder(InvocationExpressionSyntax originalNode, par } } }; - } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaParamDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaParamDescription.cs index 8afe880..ffcdf32 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaParamDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/LambdaParamDescription.cs @@ -4,446 +4,444 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +interface IParamDescription { - interface IParamDescription - { - public ParameterSyntax Syntax { get; } - public ITypeSymbol TypeSymbol { get; } - public string Name { get; } - public bool IsByRef(); - } + public ParameterSyntax Syntax { get; } + public ITypeSymbol TypeSymbol { get; } + public string Name { get; } + public bool IsByRef(); +} +/// +/// A parameter that requires : +/// * A field of type IParamDescription.TypeSymbol to be inserted in the System class +/// * logic to be inserted in the execute method +/// +/// The generator will create a unique field name composed from the field type and read-only state. +/// +/// +interface IParamRequireUpdate : IParamDescription +{ /// - /// A parameter that requires : - /// * A field of type IParamDescription.TypeSymbol to be inserted in the System class - /// * logic to be inserted in the execute method - /// - /// The generator will create a unique field name composed from the field type and read-only state. - /// + /// If the parameter is read-only. + /// Used during the creation of a unique field name /// - interface IParamRequireUpdate : IParamDescription - { - /// - /// If the parameter is read-only. - /// Used during the creation of a unique field name - /// - bool IsReadOnly { get; } - - /// - /// The field name to generate - /// Will be assigned by the execute method generator so it is a unique field name - /// - string FieldName { get; set; } - - /// - /// Format the code that will be included in the execute method - /// - /// - /// - string FormatUpdateInvocation(LambdaJobDescription description); - } + bool IsReadOnly { get; } - interface IManagedComponentParamDescription : IParamDescription - { - } + /// + /// The field name to generate + /// Will be assigned by the execute method generator so it is a unique field name + /// + string FieldName { get; set; } - interface ISharedComponentParamDescription : IParamDescription - { - } + /// + /// Format the code that will be included in the execute method + /// + /// + /// + string FormatUpdateInvocation(LambdaJobDescription description); +} - interface IComponentParamDescription : IParamDescription - { - } +interface IManagedComponentParamDescription : IParamDescription +{ +} - interface IDynamicBufferParamDescription : IParamDescription +interface ISharedComponentParamDescription : IParamDescription +{ +} + +interface IComponentParamDescription : IParamDescription +{ +} + +interface IDynamicBufferParamDescription : IParamDescription +{ +} + +public abstract class LambdaParamDescription +{ + public ParameterSyntax Syntax { get; } + public ITypeSymbol TypeSymbol { get; } + public ITypeSymbol EntityQueryTypeSymbol { get; protected set; } + public string Name => Syntax != null ? Syntax.Identifier.ToString() : string.Empty; + + // E.g. writing Entities.ForEach((ref MyComponent a, ref MyComponent b) => {}), where MyComponent is an IComponentData type, is not allowed. + // On the other hand, we allow two int parameters, e.g. when writing Entities.ForEach((Entity entity, int entityInQueryIndex, int nativeThreadIndex) => {}). + internal virtual bool AllowDuplicateTypes => false; + internal virtual bool IsSourceGeneratedParam => false; + public bool IsByRef() => Syntax.GetModifierString() == "ref"; + internal virtual bool QueryTypeIsReadOnly() => false; + internal virtual bool IsQueryableType => true; + internal virtual string FieldAssignmentInGeneratedJobChunkType() => null; + internal virtual string FieldInGeneratedJobChunkType() => null; + internal virtual string GetNativeArrayOrAccessor() => null; + + internal LambdaParamDescription(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) { + Syntax = syntax; + TypeSymbol = typeSymbol; + EntityQueryTypeSymbol = typeSymbol; } - public abstract class LambdaParamDescription + internal static LambdaParamDescription From(LambdaJobDescription description, ParameterSyntax param, string lambdaJobName) { - public ParameterSyntax Syntax { get; } - public ITypeSymbol TypeSymbol { get; } - public ITypeSymbol EntityQueryTypeSymbol { get; protected set; } - public string Name => Syntax != null ? Syntax.Identifier.ToString() : string.Empty; - - // E.g. writing Entities.ForEach((ref MyComponent a, ref MyComponent b) => {}), where MyComponent is an IComponentData type, is not allowed. - // On the other hand, we allow two int parameters, e.g. when writing Entities.ForEach((Entity entity, int entityInQueryIndex, int nativeThreadIndex) => {}). - internal virtual bool AllowDuplicateTypes => false; - internal virtual bool IsSourceGeneratedParam => false; - public bool IsByRef() => Syntax.GetModifierString() == "ref"; - internal virtual bool QueryTypeIsReadOnly() => false; - internal virtual bool IsQueryableType => true; - internal virtual string FieldAssignmentInGeneratedJobChunkType(LambdaJobDescription lambdaJobDescription) => null; - internal virtual string FieldInGeneratedJobChunkType() => null; - internal virtual string GetNativeArrayOrAccessor() => null; - - internal LambdaParamDescription(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) - { - Syntax = syntax; - TypeSymbol = typeSymbol; - EntityQueryTypeSymbol = typeSymbol; - } + var context = description.SystemDescription; + + var typeSymbol = context.SemanticModel.GetTypeInfo(param.Type).Type; - internal static LambdaParamDescription From(LambdaJobDescription description, ParameterSyntax param, string lambdaJobName) + if (typeSymbol.Is("Unity.Entities.EntityCommandBuffer.ParallelWriter")) { - var context = description.SystemDescription; + LambdaJobsErrors.DC0081(context, param.Identifier.ValueText, param.GetLocation()); + return null; + } - var typeSymbol = context.SemanticModel.GetTypeInfo(param.Type).Type; + if (typeSymbol.InheritsFromInterface("Unity.Entities.ISharedComponentData")) + return new LambdaParamDescription_SharedComponent(param, typeSymbol, lambdaJobName); + if (typeSymbol.IsDynamicBuffer()) + return new LambdaParamDescription_DynamicBuffer(param, typeSymbol, lambdaJobName); + if (typeSymbol.Is("Unity.Entities.Entity")) + return new LambdaParamDescription_Entity(param, typeSymbol, lambdaJobName); - if (typeSymbol.Is("Unity.Entities.EntityCommandBuffer.ParallelWriter")) + if (typeSymbol.IsInt()) + { + switch (param.Identifier.ValueText) { - LambdaJobsErrors.DC0081(context, param.Identifier.ValueText, param.GetLocation()); - return null; + case "entityInQueryIndex": + return new LambdaParamDescription_EntityInQueryIndex(param, typeSymbol, lambdaJobName); + case "nativeThreadIndex": + return new LambdaParamDescription_NativeThreadIndex(param, typeSymbol, lambdaJobName); + default: + LambdaJobsErrors.DC0014(context, param.GetLocation(), param.Identifier.ValueText, new[] {"entityInQueryIndex", "nativeThreadIndex"}); + return null; } + } - if (typeSymbol.InheritsFromInterface("Unity.Entities.ISharedComponentData")) - return new LambdaParamDescription_SharedComponent(param, typeSymbol, lambdaJobName); - if (typeSymbol.IsDynamicBuffer()) - return new LambdaParamDescription_DynamicBuffer(param, typeSymbol, lambdaJobName); - if (typeSymbol.Is("Unity.Entities.Entity")) - return new LambdaParamDescription_Entity(param, typeSymbol, lambdaJobName); - - if (typeSymbol.IsInt()) + if (typeSymbol.IsValueType) + { + if (typeSymbol.Is("Unity.Entities.EntityCommandBuffer")) { - switch (param.Identifier.ValueText) - { - case "entityInQueryIndex": - return new LambdaParamDescription_EntityInQueryIndex(param, typeSymbol, lambdaJobName); - case "nativeThreadIndex": - return new LambdaParamDescription_NativeThreadIndex(param, typeSymbol, lambdaJobName); - default: - LambdaJobsErrors.DC0014(context, param.GetLocation(), param.Identifier.ValueText, new[] {"entityInQueryIndex", "nativeThreadIndex"}); - return null; - } + return new LambdaParamDescription_EntityCommandBuffer(param, typeSymbol, lambdaJobName); } - if (typeSymbol.IsValueType) + if (typeSymbol.InheritsFromInterface("Unity.Entities.IComponentData")) { - if (typeSymbol.Is("Unity.Entities.EntityCommandBuffer")) + // TODO: we can probably loosen this restriction with source generators (and allow generic IComponentData within reason), needs tests and validation + if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.TypeArguments.Any()) { - return new LambdaParamDescription_EntityCommandBuffer(param, typeSymbol, lambdaJobName); + LambdaJobsErrors.DC0050(context, param.GetLocation(), typeSymbol.Name, description.LambdaJobKind); + return null; } - if (typeSymbol.InheritsFromInterface("Unity.Entities.IComponentData")) - { - // TODO: we can probably loosen this restriction with source generators (and allow generic IComponentData within reason), needs tests and validation - if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.TypeArguments.Any()) - { - LambdaJobsErrors.DC0050(context, param.GetLocation(), typeSymbol.Name, description.LambdaJobKind); - return null; - } - - if (typeSymbol.IsZeroSizedComponent()) - return new LambdaParamDescription_TagComponent(param, typeSymbol, lambdaJobName); - return new LambdaParamDescription_Component(param, typeSymbol, lambdaJobName); - } + if (typeSymbol.IsZeroSizedComponent()) + return new LambdaParamDescription_TagComponent(param, typeSymbol, lambdaJobName); + return new LambdaParamDescription_Component(param, typeSymbol, lambdaJobName); + } - if (typeSymbol.IsAspect()) + if (typeSymbol.IsAspect()) + { + foreach (var modifier in param.Modifiers) { - foreach (var modifier in param.Modifiers) + if (modifier.IsKind(SyntaxKind.InKeyword) || modifier.IsKind(SyntaxKind.RefKeyword)) { - if (modifier.IsKind(SyntaxKind.InKeyword) || modifier.IsKind(SyntaxKind.RefKeyword)) - { - LambdaJobsErrors.DC0082(context, param.GetLocation(), param.Identifier.ValueText); - return null; - } + LambdaJobsErrors.DC0082(context, param.GetLocation(), param.Identifier.ValueText); + return null; } - - return new LambdaParamDescription_Aspect(param, typeSymbol, lambdaJobName); } - if (typeSymbol.InheritsFromInterface("Unity.Entities.IBufferElementData")) - LambdaJobsErrors.DC0033(context, param.GetLocation(), param.Identifier.ValueText, typeSymbol.Name); - else - { - if (typeSymbol is ITypeParameterSymbol) - LambdaJobsErrors.DC0050(context, param.GetLocation(), typeSymbol.Name, description.LambdaJobKind); - else - LambdaJobsErrors.DC0021(context, param.GetLocation(), param.Identifier.ValueText, typeSymbol.Name); - } - return null; + return new LambdaParamDescription_Aspect(param, typeSymbol, lambdaJobName); } - if (typeSymbol.InheritsFromInterface("Unity.Entities.IComponentData") || typeSymbol.InheritsFromType("UnityEngine.Object")) + if (typeSymbol.InheritsFromInterface("Unity.Entities.IBufferElementData")) + LambdaJobsErrors.DC0033(context, param.GetLocation(), param.Identifier.ValueText, typeSymbol.Name); + else { - // TODO: we can probably loosen this restriction with source generators (and allow generic IComponentData within reason), needs tests and validation - if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.TypeArguments.Any()) - { + if (typeSymbol is ITypeParameterSymbol) LambdaJobsErrors.DC0050(context, param.GetLocation(), typeSymbol.Name, description.LambdaJobKind); - return null; - } - return new LambdaParamDescription_ManagedComponent(param, typeSymbol, lambdaJobName); + else + LambdaJobsErrors.DC0021(context, param.GetLocation(), param.Identifier.ValueText, typeSymbol.Name); } - - LambdaJobsErrors.DC0005(context, param.GetLocation(), param.Identifier.ToString(), typeSymbol.ToDisplayString(), description.LambdaJobKind); return null; } - internal virtual string LambdaBodyMethodParameter(bool usesBurst) => null; - internal virtual string LambdaBodyParameterSetup() => null; - internal virtual string LambdaBodyParameter() => null; - internal virtual string StructuralChanges_ReadLambdaParam() => null; - internal virtual string StructuralChanges_WriteBackLambdaParam() => null; - internal virtual string StructuralChanges_LambdaBodyParameter() => null; - internal virtual string StructuralChanges_GetTypeIndex() => null; - } - - public class LambdaParamDescription_BatchIndex : LambdaParamDescription - { - // Notice that we pass `batchIndex` instead of `entityInQueryIndex` as the sort key in our replacement code for performance reasons: - // It is much faster to sort e.g. 10 batches than to sort 100,000 entities. - internal override string LambdaBodyParameter() => "batchIndex"; - internal override string LambdaBodyMethodParameter(bool _) => "int __sortKey"; - internal override bool IsSourceGeneratedParam => true; - - public LambdaParamDescription_BatchIndex() : base(default, default, string.Empty) + if (typeSymbol.InheritsFromInterface("Unity.Entities.IComponentData") || typeSymbol.InheritsFromType("UnityEngine.Object")) { + // TODO: we can probably loosen this restriction with source generators (and allow generic IComponentData within reason), needs tests and validation + if (typeSymbol is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.TypeArguments.Any()) + { + LambdaJobsErrors.DC0050(context, param.GetLocation(), typeSymbol.Name, description.LambdaJobKind); + return null; + } + return new LambdaParamDescription_ManagedComponent(param, typeSymbol, lambdaJobName); } - internal override bool IsQueryableType => false; - } - public class LambdaParamDescription_EntityCommandBuffer : LambdaParamDescription - { - public const string GeneratedParallelWriterFieldNameInJobChunkType = "__ecbParallelWriter"; - public const string GeneratedEcbFieldNameInJobChunkType = "__entityCommandBuffer"; - public const string TemporaryJobEntityCommandBufferVariableName = "__tempJobEcb"; + LambdaJobsErrors.DC0005(context, param.GetLocation(), param.Identifier.ToString(), typeSymbol.ToDisplayString(), description.LambdaJobKind); + return null; + } - public LambdaParamDescription_EntityCommandBuffer(ParameterSyntax parameterSyntax, ITypeSymbol typeSymbol, string lambdaJobName) - : base(parameterSyntax, typeSymbol, lambdaJobName) - { - } + internal virtual string LambdaBodyMethodParameter(bool usesBurst) => null; + internal virtual string LambdaBodyParameterSetup() => null; + internal virtual string LambdaBodyParameter() => null; + internal virtual string StructuralChanges_ReadLambdaParam() => null; + internal virtual string StructuralChanges_WriteBackLambdaParam() => null; + internal virtual string StructuralChanges_LambdaBodyParameter() => null; + internal virtual string StructuralChanges_GetTypeIndex() => null; +} - public string GeneratedEcbFieldNameInSystemBaseType { get; set; } - public (bool IsImmediate, ScheduleMode ScheduleMode, ITypeSymbol SystemType) Playback { get; set; } +public class LambdaParamDescription_BatchIndex : LambdaParamDescription +{ + // Notice that we pass `batchIndex` instead of `entityInQueryIndex` as the sort key in our replacement code for performance reasons: + // It is much faster to sort e.g. 10 batches than to sort 100,000 entities. + internal override string LambdaBodyParameter() => "batchIndex"; + internal override string LambdaBodyMethodParameter(bool _) => "int __sortKey"; + internal override bool IsSourceGeneratedParam => true; - internal override string FieldAssignmentInGeneratedJobChunkType(LambdaJobDescription _) - { - switch (Playback.ScheduleMode) - { - case ScheduleMode.ScheduleParallel: - return $@"{GeneratedParallelWriterFieldNameInJobChunkType} = {GeneratedEcbFieldNameInSystemBaseType}.CreateCommandBuffer().AsParallelWriter()"; - case ScheduleMode.Schedule: - return $@"{GeneratedEcbFieldNameInJobChunkType} = {GeneratedEcbFieldNameInSystemBaseType}.CreateCommandBuffer()"; - default: - return - Playback.SystemType == null - ? $@"{GeneratedEcbFieldNameInJobChunkType} = {TemporaryJobEntityCommandBufferVariableName}" - : $@"{GeneratedEcbFieldNameInJobChunkType} = {GeneratedEcbFieldNameInSystemBaseType}.CreateCommandBuffer()"; - } - } + public LambdaParamDescription_BatchIndex() : base(default, default, string.Empty) + { + } + internal override bool IsQueryableType => false; +} - internal override string FieldInGeneratedJobChunkType() => - Playback.ScheduleMode == ScheduleMode.ScheduleParallel - ? $"public global::Unity.Entities.EntityCommandBuffer.ParallelWriter {GeneratedParallelWriterFieldNameInJobChunkType};" - : $"public global::Unity.Entities.EntityCommandBuffer {GeneratedEcbFieldNameInJobChunkType};"; +public class LambdaParamDescription_EntityCommandBuffer : LambdaParamDescription +{ + public const string GeneratedParallelWriterFieldNameInJobChunkType = "__ecbParallelWriter"; + public const string GeneratedEcbFieldNameInJobChunkType = "__entityCommandBuffer"; + public const string TemporaryJobEntityCommandBufferVariableName = "__tempJobEcb"; - internal override bool IsQueryableType => false; - internal override bool QueryTypeIsReadOnly() => true; + public LambdaParamDescription_EntityCommandBuffer(ParameterSyntax parameterSyntax, ITypeSymbol typeSymbol, string lambdaJobName) + : base(parameterSyntax, typeSymbol, lambdaJobName) + { } + public string GeneratedEcbFieldNameInSystemBaseType { get; set; } + public (bool IsImmediate, SystemGenerator.LambdaJobs.ScheduleMode ScheduleMode, ITypeSymbol SystemType) Playback { get; set; } - class LambdaParamDescription_Component : LambdaParamDescription, IComponentParamDescription, IParamRequireUpdate + internal override string FieldAssignmentInGeneratedJobChunkType() { - internal LambdaParamDescription_Component(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) {} - internal override string FieldInGeneratedJobChunkType() => $@"{(Syntax.IsReadOnly() ? "[global::Unity.Collections.ReadOnly] " : "")}public global::Unity.Entities.ComponentTypeHandle<{TypeSymbol.ToFullName()}> __{Syntax.Identifier}TypeHandle;"; - internal override bool QueryTypeIsReadOnly() => Syntax.IsReadOnly(); - internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"{"[Unity.Burst.NoAlias] ".EmitIfTrue(usesBurst)}{Syntax.GetModifierString()} {TypeSymbol.ToFullName()} {Syntax.Identifier}"; - internal override string GetNativeArrayOrAccessor() - { - return QueryTypeIsReadOnly() - ? $@"var {Syntax.Identifier}ArrayPtr = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtr<{TypeSymbol.ToFullName()}>(chunk, ref __{Syntax.Identifier}TypeHandle);" - : $@"var {Syntax.Identifier}ArrayPtr = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr<{TypeSymbol.ToFullName()}>(chunk, ref __{Syntax.Identifier}TypeHandle);"; - } - internal override string LambdaBodyParameter() + switch (Playback.ScheduleMode) { - return Syntax.GetModifierString() switch - { - "ref" => $@"ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement<{TypeSymbol.ToFullName()}>({Syntax.Identifier}ArrayPtr, entityIndex)", - "in" => $@"in global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement<{TypeSymbol.ToFullName()}>({Syntax.Identifier}ArrayPtr, entityIndex)", - _ => $@"global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement<{TypeSymbol.ToFullName()}>({Syntax.Identifier}ArrayPtr, entityIndex)" - }; + case ScheduleMode.ScheduleParallel: + return $@"{GeneratedParallelWriterFieldNameInJobChunkType} = {GeneratedEcbFieldNameInSystemBaseType}.CreateCommandBuffer().AsParallelWriter()"; + case ScheduleMode.Schedule: + return $@"{GeneratedEcbFieldNameInJobChunkType} = {GeneratedEcbFieldNameInSystemBaseType}.CreateCommandBuffer()"; + default: + return + Playback.SystemType == null + ? $@"{GeneratedEcbFieldNameInJobChunkType} = {TemporaryJobEntityCommandBufferVariableName}" + : $@"{GeneratedEcbFieldNameInJobChunkType} = {GeneratedEcbFieldNameInSystemBaseType}.CreateCommandBuffer()"; } + } - internal override string FieldAssignmentInGeneratedJobChunkType(LambdaJobDescription lambdaJobDescription) => - $@"__{Syntax.Identifier}TypeHandle = __TypeHandle.{FieldName}"; + internal override string FieldInGeneratedJobChunkType() => + Playback.ScheduleMode == ScheduleMode.ScheduleParallel + ? $"public global::Unity.Entities.EntityCommandBuffer.ParallelWriter {GeneratedParallelWriterFieldNameInJobChunkType};" + : $"public global::Unity.Entities.EntityCommandBuffer {GeneratedEcbFieldNameInJobChunkType};"; - internal override string StructuralChanges_GetTypeIndex() => - $@"var {Syntax.Identifier}TypeIndex = global::Unity.Entities.TypeManager.GetTypeIndex<{TypeSymbol.ToFullName()}>();"; - internal override string StructuralChanges_ReadLambdaParam() => - $@"var {Syntax.Identifier} = global::Unity.Entities.Internal.InternalCompilerInterface.GetComponentData<{TypeSymbol.ToFullName()}>(__this.EntityManager, entity, {Syntax.Identifier}TypeIndex, out var original{Syntax.Identifier});"; - internal override string StructuralChanges_WriteBackLambdaParam() => - Syntax.IsReadOnly() ? null : $@"global::Unity.Entities.Internal.InternalCompilerInterface.WriteComponentData<{TypeSymbol.ToFullName()}>(__this.EntityManager, entity, {Syntax.Identifier}TypeIndex, ref {Syntax.Identifier}, ref original{Syntax.Identifier});"; - internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.GetModifierString()} {Syntax.Identifier}"; + internal override bool IsQueryableType => false; + internal override bool QueryTypeIsReadOnly() => true; +} - bool IParamRequireUpdate.IsReadOnly => QueryTypeIsReadOnly(); - public string FieldName { get; set; } - string IParamRequireUpdate.FormatUpdateInvocation(LambdaJobDescription description) + +class LambdaParamDescription_Component : LambdaParamDescription, IComponentParamDescription, IParamRequireUpdate +{ + internal LambdaParamDescription_Component(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) {} + internal override string FieldInGeneratedJobChunkType() => $@"{(Syntax.IsReadOnly() ? "[global::Unity.Collections.ReadOnly] " : "")}public global::Unity.Entities.ComponentTypeHandle<{TypeSymbol.ToFullName()}> __{Syntax.Identifier}TypeHandle;"; + internal override bool QueryTypeIsReadOnly() => Syntax.IsReadOnly(); + internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"{"[Unity.Burst.NoAlias] ".EmitIfTrue(usesBurst)}{Syntax.GetModifierString()} {TypeSymbol.ToFullName()} {Syntax.Identifier}"; + internal override string GetNativeArrayOrAccessor() + { + return QueryTypeIsReadOnly() + ? $@"var {Syntax.Identifier}ArrayPtr = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtr<{TypeSymbol.ToFullName()}>(chunk, ref __{Syntax.Identifier}TypeHandle);" + : $@"var {Syntax.Identifier}ArrayPtr = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr<{TypeSymbol.ToFullName()}>(chunk, ref __{Syntax.Identifier}TypeHandle);"; + } + internal override string LambdaBodyParameter() + { + return Syntax.GetModifierString() switch { - return $@"__TypeHandle.{FieldName}.Update(ref this.CheckedStateRef);"; - } + "ref" => $@"ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement<{TypeSymbol.ToFullName()}>({Syntax.Identifier}ArrayPtr, entityIndex)", + "in" => $@"in global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement<{TypeSymbol.ToFullName()}>({Syntax.Identifier}ArrayPtr, entityIndex)", + _ => $@"global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement<{TypeSymbol.ToFullName()}>({Syntax.Identifier}ArrayPtr, entityIndex)" + }; } - class LambdaParamDescription_TagComponent : LambdaParamDescription, IComponentParamDescription + internal override string FieldAssignmentInGeneratedJobChunkType() => + $@"__{Syntax.Identifier}TypeHandle = __TypeHandle.{FieldName}"; + + internal override string StructuralChanges_GetTypeIndex() => + $@"var {Syntax.Identifier}TypeIndex = global::Unity.Entities.TypeManager.GetTypeIndex<{TypeSymbol.ToFullName()}>();"; + internal override string StructuralChanges_ReadLambdaParam() => + $@"var {Syntax.Identifier} = global::Unity.Entities.Internal.InternalCompilerInterface.GetComponentData<{TypeSymbol.ToFullName()}>(__this.EntityManager, entity, {Syntax.Identifier}TypeIndex, out var original{Syntax.Identifier});"; + internal override string StructuralChanges_WriteBackLambdaParam() => + Syntax.IsReadOnly() ? null : $@"global::Unity.Entities.Internal.InternalCompilerInterface.WriteComponentData<{TypeSymbol.ToFullName()}>(__this.EntityManager, entity, {Syntax.Identifier}TypeIndex, ref {Syntax.Identifier}, ref original{Syntax.Identifier});"; + internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.GetModifierString()} {Syntax.Identifier}"; + + bool IParamRequireUpdate.IsReadOnly => QueryTypeIsReadOnly(); + public string FieldName { get; set; } + string IParamRequireUpdate.FormatUpdateInvocation(LambdaJobDescription description) { - internal LambdaParamDescription_TagComponent(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) {} - internal override bool QueryTypeIsReadOnly() => true; - internal override string LambdaBodyMethodParameter(bool usesBurst) => - $@"{"[Unity.Burst.NoAlias] ".EmitIfTrue(usesBurst)}{Syntax.GetModifierString()} {TypeSymbol.ToFullName()} {Syntax.Identifier}"; - internal override string LambdaBodyParameterSetup() => $@"{TypeSymbol.ToFullName()} {Syntax.Identifier} = default;"; - internal override string LambdaBodyParameter() => $"{Syntax.GetModifierString()} {Syntax.Identifier}"; - internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.GetModifierString()} {Syntax.Identifier}"; + return $@"__TypeHandle.{FieldName}.Update(ref this.CheckedStateRef);"; } +} + +class LambdaParamDescription_TagComponent : LambdaParamDescription, IComponentParamDescription +{ + internal LambdaParamDescription_TagComponent(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) {} + internal override bool QueryTypeIsReadOnly() => true; + internal override string LambdaBodyMethodParameter(bool usesBurst) => + $@"{"[Unity.Burst.NoAlias] ".EmitIfTrue(usesBurst)}{Syntax.GetModifierString()} {TypeSymbol.ToFullName()} {Syntax.Identifier}"; + internal override string LambdaBodyParameterSetup() => $@"{TypeSymbol.ToFullName()} {Syntax.Identifier} = default;"; + internal override string LambdaBodyParameter() => $"{Syntax.GetModifierString()} {Syntax.Identifier}"; + internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.GetModifierString()} {Syntax.Identifier}"; +} + +class LambdaParamDescription_ManagedComponent : LambdaParamDescription, IManagedComponentParamDescription +{ + internal LambdaParamDescription_ManagedComponent(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) {} + internal override string FieldInGeneratedJobChunkType() => $@"public global::Unity.Entities.ComponentTypeHandle<{TypeSymbol.ToFullName()}> __{Syntax.Identifier}TypeHandle;"; + internal override bool QueryTypeIsReadOnly() => Syntax.IsReadOnly(); + internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"{TypeSymbol.ToFullName()} {Syntax.Identifier}"; + internal override string GetNativeArrayOrAccessor() => + $@"var {Syntax.Identifier}Accessor = chunk.GetManagedComponentAccessor(ref __{Syntax.Identifier}TypeHandle, __this.EntityManager);"; + internal override string LambdaBodyParameter() => $@"{Syntax.Identifier}Accessor[entityIndex]"; + internal override string FieldAssignmentInGeneratedJobChunkType() => + $@"__{Syntax.Identifier}TypeHandle = this.EntityManager.GetComponentTypeHandle<{TypeSymbol.ToFullName()}>({(Syntax.IsReadOnly() ? "true" : "false")})"; + internal override string StructuralChanges_ReadLambdaParam() => + $@"var {Syntax.Identifier} = __this.EntityManager.GetComponentObject<{TypeSymbol.ToFullName()}>(entity);"; + internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.Identifier}"; +} + +class LambdaParamDescription_SharedComponent : LambdaParamDescription, ISharedComponentParamDescription +{ + internal LambdaParamDescription_SharedComponent(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) {} + internal override string FieldInGeneratedJobChunkType() => + $@"{(Syntax.IsReadOnly() ? "[Unity.Collections.ReadOnly] " : "")}public global::Unity.Entities.SharedComponentTypeHandle<{TypeSymbol.ToFullName()}> __{Syntax.Identifier}TypeHandle;"; + internal override bool QueryTypeIsReadOnly() => Syntax.IsReadOnly(); + internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"{Syntax.GetModifierString()} {TypeSymbol.ToFullName()} {Syntax.Identifier}"; + internal override string GetNativeArrayOrAccessor() => + $@"var __{Syntax.Identifier}Data = chunk.GetSharedComponentManaged(__{Syntax.Identifier}TypeHandle, __this.EntityManager);"; + internal override string LambdaBodyParameter() => $"__{Syntax.Identifier}Data"; + internal override string FieldAssignmentInGeneratedJobChunkType() => $@"__{Syntax.Identifier}TypeHandle = GetSharedComponentTypeHandle<{TypeSymbol.ToFullName()}>()"; + + internal override string StructuralChanges_ReadLambdaParam() => + $@"var {Syntax.Identifier} = __this.EntityManager.GetSharedComponentManaged<{TypeSymbol.ToFullName()}>(entity);"; + internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.GetModifierString()} {Syntax.Identifier}"; +} - class LambdaParamDescription_ManagedComponent : LambdaParamDescription, IManagedComponentParamDescription +class LambdaParamDescription_DynamicBuffer : LambdaParamDescription, IDynamicBufferParamDescription, IParamRequireUpdate +{ + ITypeSymbol _bufferGenericArgumentType; + internal LambdaParamDescription_DynamicBuffer(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) { - internal LambdaParamDescription_ManagedComponent(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) {} - internal override string FieldInGeneratedJobChunkType() => $@"public global::Unity.Entities.ComponentTypeHandle<{TypeSymbol.ToFullName()}> __{Syntax.Identifier}TypeHandle;"; - internal override bool QueryTypeIsReadOnly() => Syntax.IsReadOnly(); - internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"{TypeSymbol.ToFullName()} {Syntax.Identifier}"; - internal override string GetNativeArrayOrAccessor() => - $@"var {Syntax.Identifier}Accessor = chunk.GetManagedComponentAccessor(ref __{Syntax.Identifier}TypeHandle, __this.EntityManager);"; - internal override string LambdaBodyParameter() => $@"{Syntax.Identifier}Accessor[entityIndex]"; - internal override string FieldAssignmentInGeneratedJobChunkType(LambdaJobDescription lambdaJobDescription) => - $@"__{Syntax.Identifier}TypeHandle = this.EntityManager.GetComponentTypeHandle<{TypeSymbol.ToFullName()}>({(Syntax.IsReadOnly() ? "true" : "false")})"; - internal override string StructuralChanges_ReadLambdaParam() => - $@"var {Syntax.Identifier} = __this.EntityManager.GetComponentObject<{TypeSymbol.ToFullName()}>(entity);"; - internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.Identifier}"; + var namedTypeSymbol = (INamedTypeSymbol)typeSymbol; + _bufferGenericArgumentType = namedTypeSymbol.TypeArguments.First(); + EntityQueryTypeSymbol = _bufferGenericArgumentType; } - - class LambdaParamDescription_SharedComponent : LambdaParamDescription, ISharedComponentParamDescription + internal override string FieldInGeneratedJobChunkType() => + $@"public BufferTypeHandle<{_bufferGenericArgumentType}> __{Syntax.Identifier}TypeHandle;"; + internal override bool QueryTypeIsReadOnly() => Syntax.IsReadOnly(); + internal override string LambdaBodyMethodParameter(bool usesBurst) => + $@"DynamicBuffer<{_bufferGenericArgumentType}> {Syntax.Identifier}"; + internal override string GetNativeArrayOrAccessor() => + $@"var {Syntax.Identifier}Accessor = chunk.GetBufferAccessor(ref __{Syntax.Identifier}TypeHandle);"; + internal override string LambdaBodyParameter() => $@"{Syntax.Identifier}Accessor[entityIndex]"; + + internal override string FieldAssignmentInGeneratedJobChunkType() => + $@"__{Syntax.Identifier}TypeHandle = __TypeHandle.{FieldName}"; + + bool IParamRequireUpdate.IsReadOnly => QueryTypeIsReadOnly(); + public string FieldName { get; set; } + string IParamRequireUpdate.FormatUpdateInvocation(LambdaJobDescription description) { - internal LambdaParamDescription_SharedComponent(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) {} - internal override string FieldInGeneratedJobChunkType() => - $@"{(Syntax.IsReadOnly() ? "[Unity.Collections.ReadOnly] " : "")}public global::Unity.Entities.SharedComponentTypeHandle<{TypeSymbol.ToFullName()}> __{Syntax.Identifier}TypeHandle;"; - internal override bool QueryTypeIsReadOnly() => Syntax.IsReadOnly(); - internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"{Syntax.GetModifierString()} {TypeSymbol.ToFullName()} {Syntax.Identifier}"; - internal override string GetNativeArrayOrAccessor() => - $@"var __{Syntax.Identifier}Data = chunk.GetSharedComponentManaged(__{Syntax.Identifier}TypeHandle, __this.EntityManager);"; - internal override string LambdaBodyParameter() => $"__{Syntax.Identifier}Data"; - internal override string FieldAssignmentInGeneratedJobChunkType(LambdaJobDescription lambdaJobDescription) => $@"__{Syntax.Identifier}TypeHandle = GetSharedComponentTypeHandle<{TypeSymbol.ToFullName()}>()"; - - internal override string StructuralChanges_ReadLambdaParam() => - $@"var {Syntax.Identifier} = __this.EntityManager.GetSharedComponentManaged<{TypeSymbol.ToFullName()}>(entity);"; - internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.GetModifierString()} {Syntax.Identifier}"; + return $@"__TypeHandle.{FieldName}.Update(ref this.CheckedStateRef);"; } - class LambdaParamDescription_DynamicBuffer : LambdaParamDescription, IDynamicBufferParamDescription, IParamRequireUpdate - { - ITypeSymbol _bufferGenericArgumentType; - internal LambdaParamDescription_DynamicBuffer(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) - { - var namedTypeSymbol = (INamedTypeSymbol)typeSymbol; - _bufferGenericArgumentType = namedTypeSymbol.TypeArguments.First(); - EntityQueryTypeSymbol = _bufferGenericArgumentType; - } - internal override string FieldInGeneratedJobChunkType() => - $@"public BufferTypeHandle<{_bufferGenericArgumentType}> __{Syntax.Identifier}TypeHandle;"; - internal override bool QueryTypeIsReadOnly() => Syntax.IsReadOnly(); - internal override string LambdaBodyMethodParameter(bool usesBurst) => - $@"DynamicBuffer<{_bufferGenericArgumentType}> {Syntax.Identifier}"; - internal override string GetNativeArrayOrAccessor() => - $@"var {Syntax.Identifier}Accessor = chunk.GetBufferAccessor(ref __{Syntax.Identifier}TypeHandle);"; - internal override string LambdaBodyParameter() => $@"{Syntax.Identifier}Accessor[entityIndex]"; - - internal override string FieldAssignmentInGeneratedJobChunkType(LambdaJobDescription lambdaJobDescription) => - $@"__{Syntax.Identifier}TypeHandle = __TypeHandle.{FieldName}"; - - bool IParamRequireUpdate.IsReadOnly => QueryTypeIsReadOnly(); - public string FieldName { get; set; } - string IParamRequireUpdate.FormatUpdateInvocation(LambdaJobDescription description) - { - return $@"__TypeHandle.{FieldName}.Update(ref this.CheckedStateRef);"; - } + internal override string StructuralChanges_ReadLambdaParam() => + $@"var {Syntax.Identifier} = __this.EntityManager.GetBuffer<{_bufferGenericArgumentType}>(entity);"; + internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.Identifier}"; +} - internal override string StructuralChanges_ReadLambdaParam() => - $@"var {Syntax.Identifier} = __this.EntityManager.GetBuffer<{_bufferGenericArgumentType}>(entity);"; - internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.Identifier}"; +class LambdaParamDescription_Entity : LambdaParamDescription, IParamRequireUpdate +{ + internal LambdaParamDescription_Entity(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) {} + internal override string FieldInGeneratedJobChunkType() => $@"[global::Unity.Collections.ReadOnly] public global::Unity.Entities.EntityTypeHandle __{Syntax.Identifier}TypeHandle;"; + internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"global::Unity.Entities.Entity {Syntax.Identifier}"; + internal override string LambdaBodyParameter() => + $@"global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement(__{Syntax.Identifier}ArrayPtr, entityIndex)"; + internal override string GetNativeArrayOrAccessor() => + $@"var __{Syntax.Identifier}ArrayPtr = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkEntityArrayIntPtr(chunk, __{Syntax.Identifier}TypeHandle);"; + + internal override string FieldAssignmentInGeneratedJobChunkType() + { + return $@"__{Syntax.Identifier}TypeHandle = __TypeHandle.{FieldName}"; } - class LambdaParamDescription_Entity : LambdaParamDescription, IParamRequireUpdate - { - internal LambdaParamDescription_Entity(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) {} - internal override string FieldInGeneratedJobChunkType() => $@"[global::Unity.Collections.ReadOnly] public global::Unity.Entities.EntityTypeHandle __{Syntax.Identifier}TypeHandle;"; - internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"global::Unity.Entities.Entity {Syntax.Identifier}"; - internal override string LambdaBodyParameter() => - $@"global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement(__{Syntax.Identifier}ArrayPtr, entityIndex)"; - internal override string GetNativeArrayOrAccessor() => - $@"var __{Syntax.Identifier}ArrayPtr = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkEntityArrayIntPtr(chunk, __{Syntax.Identifier}TypeHandle);"; - - internal override string FieldAssignmentInGeneratedJobChunkType(LambdaJobDescription lambdaJobDescription) - { - return $@"__{Syntax.Identifier}TypeHandle = __TypeHandle.{FieldName}"; - } + internal override string StructuralChanges_LambdaBodyParameter() => $@"entity"; - internal override string StructuralChanges_LambdaBodyParameter() => $@"entity"; + internal override bool IsQueryableType => false; - internal override bool IsQueryableType => false; + public bool IsReadOnly => true; + public string FieldName { get; set; } - public bool IsReadOnly => true; - public string FieldName { get; set; } + public string FormatUpdateInvocation(LambdaJobDescription description) => $@"__TypeHandle.{FieldName}.Update(ref this.CheckedStateRef);"; - public string FormatUpdateInvocation(LambdaJobDescription description) => $@"__TypeHandle.{FieldName}.Update(ref this.CheckedStateRef);"; +} - } +class LambdaParamDescription_EntityInQueryIndex : LambdaParamDescription +{ + internal LambdaParamDescription_EntityInQueryIndex(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) { } + internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"int {Syntax.Identifier}"; + internal override string LambdaBodyParameter() => @"entityInQueryIndex"; + internal override string StructuralChanges_LambdaBodyParameter() => @"entityIndex"; + internal override bool AllowDuplicateTypes => true; + internal override bool IsQueryableType => false; +} - class LambdaParamDescription_EntityInQueryIndex : LambdaParamDescription +class LambdaParamDescription_NativeThreadIndex : LambdaParamDescription +{ + internal LambdaParamDescription_NativeThreadIndex(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) { } + internal override string FieldInGeneratedJobChunkType() => + @"[global::Unity.Collections.LowLevel.Unsafe.NativeSetThreadIndexAttribute] internal int __NativeThreadIndex;"; + internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"int {Syntax.Identifier}"; + internal override string LambdaBodyParameter() => $@"__NativeThreadIndex"; + internal override string FieldAssignmentInGeneratedJobChunkType() => $@"__NativeThreadIndex = 0"; + internal override string StructuralChanges_LambdaBodyParameter() => @"__NativeThreadIndex"; + internal override bool AllowDuplicateTypes => true; + internal override bool IsQueryableType => false; +} + + +class LambdaParamDescription_Aspect : LambdaParamDescription, IParamRequireUpdate +{ + internal LambdaParamDescription_Aspect(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) { } + + internal override string FieldInGeneratedJobChunkType() => $@"{(Syntax.IsReadOnly() ? "[Unity.Collections.ReadOnly] " : "")}public {TypeSymbol.ToFullName()}.TypeHandle __{Syntax.Identifier}TypeHandle;"; + internal override bool QueryTypeIsReadOnly() => Syntax.IsReadOnly(); + internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"{TypeSymbol.ToFullName()} {Syntax.Identifier}"; + internal override string GetNativeArrayOrAccessor() { - internal LambdaParamDescription_EntityInQueryIndex(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) { } - internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"int {Syntax.Identifier}"; - internal override string LambdaBodyParameter() => @"entityInQueryIndex"; - internal override string StructuralChanges_LambdaBodyParameter() => @"entityIndex"; - internal override bool AllowDuplicateTypes => true; - internal override bool IsQueryableType => false; + return $@"var {Syntax.Identifier}ArrayPtr = __{Syntax.Identifier}TypeHandle.Resolve(chunk);"; } - - class LambdaParamDescription_NativeThreadIndex : LambdaParamDescription + internal override string LambdaBodyParameter() { - internal LambdaParamDescription_NativeThreadIndex(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) { } - internal override string FieldInGeneratedJobChunkType() => - @"[global::Unity.Collections.LowLevel.Unsafe.NativeSetThreadIndexAttribute] internal int __NativeThreadIndex;"; - internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"int {Syntax.Identifier}"; - internal override string LambdaBodyParameter() => $@"__NativeThreadIndex"; - internal override string FieldAssignmentInGeneratedJobChunkType(LambdaJobDescription lambdaJobDescription) => $@"__NativeThreadIndex = 0"; - internal override string StructuralChanges_LambdaBodyParameter() => @"__NativeThreadIndex"; - internal override bool AllowDuplicateTypes => true; - internal override bool IsQueryableType => false; + return $@"{Syntax.Identifier}ArrayPtr[entityIndex]"; } + internal override string FieldAssignmentInGeneratedJobChunkType() => + $@"__{Syntax.Identifier}TypeHandle = __TypeHandle.{FieldName}"; - class LambdaParamDescription_Aspect : LambdaParamDescription, IParamRequireUpdate + internal override string StructuralChanges_ReadLambdaParam() { - internal LambdaParamDescription_Aspect(ParameterSyntax syntax, ITypeSymbol typeSymbol, string lambdaJobName) : base(syntax, typeSymbol, lambdaJobName) { } - - internal override string FieldInGeneratedJobChunkType() => $@"{(Syntax.IsReadOnly() ? "[Unity.Collections.ReadOnly] " : "")}public {TypeSymbol.ToFullName()}.TypeHandle __{Syntax.Identifier}TypeHandle;"; - internal override bool QueryTypeIsReadOnly() => Syntax.IsReadOnly(); - internal override string LambdaBodyMethodParameter(bool usesBurst) => $@"{TypeSymbol.ToFullName()} {Syntax.Identifier}"; - internal override string GetNativeArrayOrAccessor() - { - return $@"var {Syntax.Identifier}ArrayPtr = __{Syntax.Identifier}TypeHandle.Resolve(chunk);"; - } - internal override string LambdaBodyParameter() - { - return $@"{Syntax.Identifier}ArrayPtr[entityIndex]"; - } - - internal override string FieldAssignmentInGeneratedJobChunkType(LambdaJobDescription lambdaJobDescription) => - $@"__{Syntax.Identifier}TypeHandle = __TypeHandle.{FieldName}"; - - internal override string StructuralChanges_ReadLambdaParam() - { - return $@"var {Syntax.Identifier} = __this.GetAspect<{TypeSymbol.ToFullName()}>(entity);"; - } - - internal override string StructuralChanges_WriteBackLambdaParam() => ""; + return $@"var {Syntax.Identifier} = __this.GetAspect<{TypeSymbol.ToFullName()}>(entity);"; + } - internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.GetModifierString()} {Syntax.Identifier}"; + internal override string StructuralChanges_WriteBackLambdaParam() => ""; - public string FieldName { get; set; } - bool IParamRequireUpdate.IsReadOnly => QueryTypeIsReadOnly(); - string IParamRequireUpdate.FormatUpdateInvocation(LambdaJobDescription description) - => $@"__TypeHandle.{FieldName}.Update(ref this.CheckedStateRef);"; - } + internal override string StructuralChanges_LambdaBodyParameter() => $@"{Syntax.GetModifierString()} {Syntax.Identifier}"; + public string FieldName { get; set; } + bool IParamRequireUpdate.IsReadOnly => QueryTypeIsReadOnly(); + string IParamRequireUpdate.FormatUpdateInvocation(LambdaJobDescription description) + => $@"__TypeHandle.{FieldName}.Update(ref this.CheckedStateRef);"; } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ParallelEcbInvocationsReplacer.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ParallelEcbInvocationsReplacer.cs index c1b48ba..beccd70 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ParallelEcbInvocationsReplacer.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/ParallelEcbInvocationsReplacer.cs @@ -2,173 +2,172 @@ using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; -using static Unity.Entities.SourceGen.LambdaJobs.ArgumentParserForEntityCommandBufferMethods; -using static Unity.Entities.SourceGen.LambdaJobs.LambdaParamDescription_EntityCommandBuffer; +using static Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.ArgumentParserForEntityCommandBufferMethods; +using static Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.LambdaParamDescription_EntityCommandBuffer; -namespace Unity.Entities.SourceGen.LambdaJobs +namespace Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; + +internal static class ParallelEcbInvocationsReplacer { - internal static class ParallelEcbInvocationsReplacer + public static InvocationExpressionSyntax CreateReplacement(InvocationExpressionSyntax originalNode, IInvocationOperation invocationOperation) { - public static InvocationExpressionSyntax CreateReplacement(InvocationExpressionSyntax originalNode, IInvocationOperation invocationOperation) - { - string replacementCode; - var parsedArguments = Parse(originalNode, invocationOperation.TargetMethod); + string replacementCode; + var parsedArguments = Parse(originalNode, invocationOperation.TargetMethod); - switch (invocationOperation.TargetMethod.Name) + switch (invocationOperation.TargetMethod.Name) + { + case "AddComponent": { - case "AddComponent": - { - replacementCode = GetReplacementCodeForAddComponentInvocation(parsedArguments); - break; - } - case "AddBuffer": - { - replacementCode = $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, e: {parsedArguments.Entity})"; - break; - } - case "AddSharedComponent": - { - replacementCode = - String.IsNullOrEmpty(parsedArguments.EntitiesNativeArray) - ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddSharedComponent(__sortKey, e: {parsedArguments.Entity}, sharedComponent: {parsedArguments.SharedComponentData})" - : $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddSharedComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, sharedComponent: {parsedArguments.SharedComponentData})"; - break; - } - case "AppendToBuffer": - { - replacementCode = $"{GeneratedParallelWriterFieldNameInJobChunkType}.AppendToBuffer(__sortKey, e: {parsedArguments.Entity}, element: {parsedArguments.BufferElementData})"; - break; - } - case "CreateEntity": - { - replacementCode = - parsedArguments.EntityArchetype == null - ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.CreateEntity(__sortKey)" - : $"{GeneratedParallelWriterFieldNameInJobChunkType}.CreateEntity(__sortKey, {parsedArguments.EntityArchetype})"; - break; - } - case "DestroyEntity": - { - replacementCode = - String.IsNullOrEmpty(parsedArguments.EntitiesNativeArray) - ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.DestroyEntity(__sortKey, e: {parsedArguments.Entity})" - : $"{GeneratedParallelWriterFieldNameInJobChunkType}.DestroyEntity(__sortKey, entities: {parsedArguments.EntitiesNativeArray})"; - break; - } - case "Instantiate": - { - replacementCode = - string.IsNullOrEmpty(parsedArguments.EntitiesNativeArray) - ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.Instantiate(__sortKey, e: {parsedArguments.Entity})" - : $"{GeneratedParallelWriterFieldNameInJobChunkType}.Instantiate(__sortKey, e: {parsedArguments.Entity}, entities: {parsedArguments.EntitiesNativeArray})"; - break; - } - case "RemoveComponent": - { - replacementCode = GetReplacementCodeForRemoveComponentInvocation(parsedArguments); - break; - } - case "SetBuffer": - { - replacementCode = $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, e: {parsedArguments.Entity})"; - break; - } - case "SetComponent": - { - replacementCode = $"{GeneratedParallelWriterFieldNameInJobChunkType}.SetComponent(__sortKey, e: {parsedArguments.Entity}, component: {parsedArguments.ComponentData})"; - break; - } - case "SetComponentEnabled": - { - replacementCode = - !String.IsNullOrEmpty(parsedArguments.GenericTypeArgument) - ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, e: {parsedArguments.Entity}, value: {parsedArguments.BooleanValue})" - : $"{GeneratedParallelWriterFieldNameInJobChunkType}.SetComponentEnabled(__sortKey, e: {parsedArguments.Entity}, componentType: {parsedArguments.ComponentType}, value: {parsedArguments.BooleanValue})"; - break; - } - case "SetName": - { - replacementCode = $"{GeneratedParallelWriterFieldNameInJobChunkType}.SetName(__sortKey, e: {parsedArguments.Entity}, name: {parsedArguments.FixedString64Bytes})"; - break; - } - case "SetSharedComponent": - { - replacementCode = - String.IsNullOrEmpty(parsedArguments.EntitiesNativeArray) - ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.SetSharedComponent(__sortKey, e: {parsedArguments.Entity}, sharedComponent: {parsedArguments.SharedComponentData})" - : $"{GeneratedParallelWriterFieldNameInJobChunkType}.SetSharedComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, sharedComponent: {parsedArguments.SharedComponentData})"; - break; - } - default: - throw new ArgumentOutOfRangeException(); + replacementCode = GetReplacementCodeForAddComponentInvocation(parsedArguments); + break; } - return (InvocationExpressionSyntax)SyntaxFactory.ParseExpression(replacementCode); - } - - static string GetReplacementCodeForAddComponentInvocation(ArgumentValues parsedArguments) - { - const string errorMessage = "Failed to parse invocation of RemoveComponent() with a generic type argument, an IComponentData argument, a ComponentType argument, OR a ComponentTypeSet argument."; - - if (String.IsNullOrEmpty(parsedArguments.EntitiesNativeArray)) + case "AddBuffer": { - if (!String.IsNullOrEmpty(parsedArguments.GenericTypeArgument)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, e: {parsedArguments.Entity})"; - - if (!String.IsNullOrEmpty(parsedArguments.ComponentType)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, e: {parsedArguments.Entity}, componentType: {parsedArguments.ComponentType})"; - - if (!String.IsNullOrEmpty(parsedArguments.ComponentTypeSet)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, e: {parsedArguments.Entity}, typeSet: {parsedArguments.ComponentTypeSet})"; - - if (!String.IsNullOrEmpty(parsedArguments.ComponentData)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, e: {parsedArguments.Entity}, component: {parsedArguments.ComponentData})"; - - throw new ParserException(errorMessage); + replacementCode = $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, e: {parsedArguments.Entity})"; + break; + } + case "AddSharedComponent": + { + replacementCode = + String.IsNullOrEmpty(parsedArguments.EntitiesNativeArray) + ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddSharedComponent(__sortKey, e: {parsedArguments.Entity}, sharedComponent: {parsedArguments.SharedComponentData})" + : $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddSharedComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, sharedComponent: {parsedArguments.SharedComponentData})"; + break; + } + case "AppendToBuffer": + { + replacementCode = $"{GeneratedParallelWriterFieldNameInJobChunkType}.AppendToBuffer(__sortKey, e: {parsedArguments.Entity}, element: {parsedArguments.BufferElementData})"; + break; + } + case "CreateEntity": + { + replacementCode = + parsedArguments.EntityArchetype == null + ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.CreateEntity(__sortKey)" + : $"{GeneratedParallelWriterFieldNameInJobChunkType}.CreateEntity(__sortKey, {parsedArguments.EntityArchetype})"; + break; + } + case "DestroyEntity": + { + replacementCode = + String.IsNullOrEmpty(parsedArguments.EntitiesNativeArray) + ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.DestroyEntity(__sortKey, e: {parsedArguments.Entity})" + : $"{GeneratedParallelWriterFieldNameInJobChunkType}.DestroyEntity(__sortKey, entities: {parsedArguments.EntitiesNativeArray})"; + break; + } + case "Instantiate": + { + replacementCode = + string.IsNullOrEmpty(parsedArguments.EntitiesNativeArray) + ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.Instantiate(__sortKey, e: {parsedArguments.Entity})" + : $"{GeneratedParallelWriterFieldNameInJobChunkType}.Instantiate(__sortKey, e: {parsedArguments.Entity}, entities: {parsedArguments.EntitiesNativeArray})"; + break; + } + case "RemoveComponent": + { + replacementCode = GetReplacementCodeForRemoveComponentInvocation(parsedArguments); + break; + } + case "SetBuffer": + { + replacementCode = $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, e: {parsedArguments.Entity})"; + break; } + case "SetComponent": + { + replacementCode = $"{GeneratedParallelWriterFieldNameInJobChunkType}.SetComponent(__sortKey, e: {parsedArguments.Entity}, component: {parsedArguments.ComponentData})"; + break; + } + case "SetComponentEnabled": + { + replacementCode = + !String.IsNullOrEmpty(parsedArguments.GenericTypeArgument) + ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, e: {parsedArguments.Entity}, value: {parsedArguments.BooleanValue})" + : $"{GeneratedParallelWriterFieldNameInJobChunkType}.SetComponentEnabled(__sortKey, e: {parsedArguments.Entity}, componentType: {parsedArguments.ComponentType}, value: {parsedArguments.BooleanValue})"; + break; + } + case "SetName": + { + replacementCode = $"{GeneratedParallelWriterFieldNameInJobChunkType}.SetName(__sortKey, e: {parsedArguments.Entity}, name: {parsedArguments.FixedString64Bytes})"; + break; + } + case "SetSharedComponent": + { + replacementCode = + String.IsNullOrEmpty(parsedArguments.EntitiesNativeArray) + ? $"{GeneratedParallelWriterFieldNameInJobChunkType}.SetSharedComponent(__sortKey, e: {parsedArguments.Entity}, sharedComponent: {parsedArguments.SharedComponentData})" + : $"{GeneratedParallelWriterFieldNameInJobChunkType}.SetSharedComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, sharedComponent: {parsedArguments.SharedComponentData})"; + break; + } + default: + throw new ArgumentOutOfRangeException(); + } + return (InvocationExpressionSyntax)SyntaxFactory.ParseExpression(replacementCode); + } + + static string GetReplacementCodeForAddComponentInvocation(ArgumentValues parsedArguments) + { + const string errorMessage = "Failed to parse invocation of RemoveComponent() with a generic type argument, an IComponentData argument, a ComponentType argument, OR a ComponentTypeSet argument."; + if (String.IsNullOrEmpty(parsedArguments.EntitiesNativeArray)) + { if (!String.IsNullOrEmpty(parsedArguments.GenericTypeArgument)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, entities: {parsedArguments.EntitiesNativeArray})"; + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, e: {parsedArguments.Entity})"; if (!String.IsNullOrEmpty(parsedArguments.ComponentType)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, componentType: {parsedArguments.ComponentType})"; + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, e: {parsedArguments.Entity}, componentType: {parsedArguments.ComponentType})"; if (!String.IsNullOrEmpty(parsedArguments.ComponentTypeSet)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, typeSet: {parsedArguments.ComponentTypeSet})"; + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, e: {parsedArguments.Entity}, typeSet: {parsedArguments.ComponentTypeSet})"; if (!String.IsNullOrEmpty(parsedArguments.ComponentData)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, component: {parsedArguments.ComponentData})"; + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, e: {parsedArguments.Entity}, component: {parsedArguments.ComponentData})"; throw new ParserException(errorMessage); } - static string GetReplacementCodeForRemoveComponentInvocation(ArgumentValues parsedArguments) - { - const string errorMessage = "Failed to parse invocation of RemoveComponent with a generic type argument, a ComponentType argument, OR a ComponentTypeSet argument."; + if (!String.IsNullOrEmpty(parsedArguments.GenericTypeArgument)) + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, entities: {parsedArguments.EntitiesNativeArray})"; - if (String.IsNullOrEmpty(parsedArguments.EntitiesNativeArray)) - { - if (!String.IsNullOrEmpty(parsedArguments.GenericTypeArgument)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, e: {parsedArguments.Entity})"; + if (!String.IsNullOrEmpty(parsedArguments.ComponentType)) + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, componentType: {parsedArguments.ComponentType})"; - if (!String.IsNullOrEmpty(parsedArguments.ComponentType)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.RemoveComponent(__sortKey, e: {parsedArguments.Entity}, componentType: {parsedArguments.ComponentType})"; + if (!String.IsNullOrEmpty(parsedArguments.ComponentTypeSet)) + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, typeSet: {parsedArguments.ComponentTypeSet})"; - if (!String.IsNullOrEmpty(parsedArguments.ComponentTypeSet)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.RemoveComponent(__sortKey, e: {parsedArguments.Entity}, typeSet: {parsedArguments.ComponentTypeSet})"; + if (!String.IsNullOrEmpty(parsedArguments.ComponentData)) + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.AddComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, component: {parsedArguments.ComponentData})"; - throw new ParserException(errorMessage); - } + throw new ParserException(errorMessage); + } + + static string GetReplacementCodeForRemoveComponentInvocation(ArgumentValues parsedArguments) + { + const string errorMessage = "Failed to parse invocation of RemoveComponent with a generic type argument, a ComponentType argument, OR a ComponentTypeSet argument."; + if (String.IsNullOrEmpty(parsedArguments.EntitiesNativeArray)) + { if (!String.IsNullOrEmpty(parsedArguments.GenericTypeArgument)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, entities: {parsedArguments.EntitiesNativeArray})"; + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, e: {parsedArguments.Entity})"; if (!String.IsNullOrEmpty(parsedArguments.ComponentType)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.RemoveComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, componentType: {parsedArguments.ComponentType})"; + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.RemoveComponent(__sortKey, e: {parsedArguments.Entity}, componentType: {parsedArguments.ComponentType})"; if (!String.IsNullOrEmpty(parsedArguments.ComponentTypeSet)) - return $"{GeneratedParallelWriterFieldNameInJobChunkType}.RemoveComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, typeSet: {parsedArguments.ComponentTypeSet})"; + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.RemoveComponent(__sortKey, e: {parsedArguments.Entity}, typeSet: {parsedArguments.ComponentTypeSet})"; throw new ParserException(errorMessage); } + + if (!String.IsNullOrEmpty(parsedArguments.GenericTypeArgument)) + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.{parsedArguments.GenericTypeArgument}(__sortKey, entities: {parsedArguments.EntitiesNativeArray})"; + + if (!String.IsNullOrEmpty(parsedArguments.ComponentType)) + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.RemoveComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, componentType: {parsedArguments.ComponentType})"; + + if (!String.IsNullOrEmpty(parsedArguments.ComponentTypeSet)) + return $"{GeneratedParallelWriterFieldNameInJobChunkType}.RemoveComponent(__sortKey, entities: {parsedArguments.EntitiesNativeArray}, typeSet: {parsedArguments.ComponentTypeSet})"; + + throw new ParserException(errorMessage); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.csproj b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.csproj index 21fdc69..245b6c5 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.csproj +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.LambdaJobs/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.csproj @@ -3,7 +3,9 @@ true netstandard2.0 - 8.0 + latest + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IFEType.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IFEType.cs index 8883875..80bf55d 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IFEType.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IFEType.cs @@ -1,218 +1,57 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Text; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +struct SharedComponentFilterInfo { - struct IFEType + public ITypeSymbol TypeSymbol { get; set; } + public ArgumentSyntax Argument { get; set; } + public bool IsManaged { get; set; } +} + +struct IfeType +{ + internal IReadOnlyCollection ReturnedTupleElementsDuringEnumeration { - IReadOnlyCollection _returnedTupleElementsDuringEnumeration; + get; + set; + } - internal IReadOnlyCollection ReturnedTupleElementsDuringEnumeration - { - get => _returnedTupleElementsDuringEnumeration; - set - { - _returnedTupleElementsDuringEnumeration = value; - StructDeclarationSyntax = Generate(); - } - } + public string TypeName { get; set; } + public string FullyQualifiedTypeName { get; set; } + public bool MustReturnEntityDuringIteration { get; set; } + public AttributeData BurstCompileAttribute { get; set; } + public bool PerformsCollectionChecks { get; set; } - public StructDeclarationSyntax StructDeclarationSyntax { get; private set; } - public string TypeName { get; set; } - public string FullyQualifiedTypeName { get; set; } - public bool MustReturnEntityDuringIteration { get; set; } - public AttributeData BurstCompileAttribute { get; set; } - public bool PerformsCollectionChecks { get; set; } + public bool UseBurst => BurstCompileAttribute != null; - private bool UseBurst => BurstCompileAttribute != null; + public (string FullName, string Creation) ResultType(IEnumerable queryResultConstructorArgs) + { + string queryReturnTypeFullName; - (string FullName, string Creation) GetResultType(IEnumerable queryResultConstructorArgs) + if (MustReturnEntityDuringIteration) { - string queryReturnTypeFullName; - - if (MustReturnEntityDuringIteration) - { - var typeParameterFullNames = ReturnedTupleElementsDuringEnumeration.Select(f => f.TypeSymbolFullName).SeparateByCommaAndSpace(); - queryReturnTypeFullName = $"Unity.Entities.QueryEnumerableWithEntity<{typeParameterFullNames}>"; - return - ( - queryReturnTypeFullName, - $"new {queryReturnTypeFullName}({queryResultConstructorArgs.SeparateByComma()})" - ); - } - - if (ReturnedTupleElementsDuringEnumeration.Count > 1) - { - queryReturnTypeFullName = - $"({ReturnedTupleElementsDuringEnumeration.Select(fieldInfo => fieldInfo.TypeSymbolFullName).SeparateByCommaAndSpace()})"; - - return (queryReturnTypeFullName, $"({queryResultConstructorArgs.SeparateByComma()})"); - } - - return (ReturnedTupleElementsDuringEnumeration.Single().TypeSymbolFullName, queryResultConstructorArgs.Single()); + var typeParameterFullNames = ReturnedTupleElementsDuringEnumeration.Select(f => f.TypeSymbolFullName).SeparateByCommaAndSpace(); + queryReturnTypeFullName = $"Unity.Entities.QueryEnumerableWithEntity<{typeParameterFullNames}>"; + return + ( + queryReturnTypeFullName, + $"new {queryReturnTypeFullName}({queryResultConstructorArgs.SeparateByComma()})" + ); } - string GenerateQueryMethod() => - "[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]" + - $"{Environment.NewLine}public static Enumerator Query(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle) => new Enumerator(entityQuery, typeHandle);"; - - string GenerateEnumerator(string queryResultTypeName) => - $@"{(UseBurst ? $"[global::Unity.Burst.NoAlias]{Environment.NewLine}[{BurstCompileAttribute}]" : string.Empty)} - public struct Enumerator : global::System.Collections.Generic.IEnumerator<{queryResultTypeName}> - {{ - global::Unity.Entities.Internal.InternalEntityQueryEnumerator _entityQueryEnumerator; - TypeHandle _typeHandle; - ResolvedChunk _resolvedChunk; - - int _currentEntityIndex; - int _endEntityIndex; - - public Enumerator(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle) - {{ - _entityQueryEnumerator = new global::Unity.Entities.Internal.InternalEntityQueryEnumerator(entityQuery); - - _currentEntityIndex = -1; - _endEntityIndex = -1; - - _typeHandle = typeHandle; - _resolvedChunk = default; - }} - - public void Dispose() => _entityQueryEnumerator.Dispose(); - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public bool MoveNext() - {{ - _currentEntityIndex++; - - {(UseBurst ? "if (global::Unity.Burst.CompilerServices.Hint.Unlikely(_currentEntityIndex >= _endEntityIndex))" : "if (_currentEntityIndex >= _endEntityIndex)")} - {{ - {(UseBurst - ? "if (global::Unity.Burst.CompilerServices.Hint.Likely(_entityQueryEnumerator.MoveNextEntityRange(out bool movedToNewChunk, out global::Unity.Entities.ArchetypeChunk chunk, out int entityStartIndex, out int entityEndIndex)))" - : "if (_entityQueryEnumerator.MoveNextEntityRange(out bool movedToNewChunk, out global::Unity.Entities.ArchetypeChunk chunk, out int entityStartIndex, out int entityEndIndex))")} - {{ - if (movedToNewChunk) - _resolvedChunk = _typeHandle.Resolve(chunk); - - _currentEntityIndex = entityStartIndex; - _endEntityIndex = entityEndIndex; - return true; - }} - return false; - }} - return true; - }} - - public {queryResultTypeName} Current => _resolvedChunk.Get(_currentEntityIndex); - - public Enumerator GetEnumerator() => this; - public void Reset() => throw new global::System.NotImplementedException(); - object global::System.Collections.IEnumerator.Current => throw new global::System.NotImplementedException(); - }}"; - - string GenerateCompleteDependenciesMethod() + if (ReturnedTupleElementsDuringEnumeration.Count > 1) { - var builder = new StringBuilder(); - builder.AppendLine("public static void CompleteDependencyBeforeRW(ref SystemState state) {"); - - foreach (var element in ReturnedTupleElementsDuringEnumeration) - { - builder.AppendLine(element.Type switch - { - QueryType.ManagedComponent => $" state.EntityManager.CompleteDependencyBeforeRW<{element.TypeSymbolFullName}>();", - QueryType.UnityEngineComponent => $" state.EntityManager.CompleteDependencyBeforeRW<{element.TypeArgumentFullName}>();", - QueryType.RefRW => $" state.EntityManager.CompleteDependencyBeforeRW<{element.TypeArgumentFullName}>();", - QueryType.RefRO => $" state.EntityManager.CompleteDependencyBeforeRO<{element.TypeArgumentFullName}>();", - QueryType.RefRO_TagComponent => $" state.EntityManager.CompleteDependencyBeforeRO<{element.TypeArgumentFullName}>();", - QueryType.RefRW_TagComponent => $" state.EntityManager.CompleteDependencyBeforeRW<{element.TypeArgumentFullName}>();", - QueryType.UnmanagedSharedComponent => $" state.EntityManager.CompleteDependencyBeforeRO<{element.TypeSymbolFullName}>();", - QueryType.ManagedSharedComponent => $" state.EntityManager.CompleteDependencyBeforeRW<{element.TypeSymbolFullName}>();", - QueryType.Aspect => $" {element.TypeSymbolFullName}.CompleteDependencyBeforeRW(ref state);", - QueryType.DynamicBuffer => $" state.EntityManager.CompleteDependencyBeforeRW<{element.TypeArgumentFullName}>();", - QueryType.ValueTypeComponent => $" state.EntityManager.CompleteDependencyBeforeRO<{element.TypeSymbolFullName}>();", - QueryType.EnabledRefRW => $" state.EntityManager.CompleteDependencyBeforeRW<{element.TypeArgumentFullName}>();", - QueryType.EnabledRefRO => $" state.EntityManager.CompleteDependencyBeforeRO<{element.TypeArgumentFullName}>();", - QueryType.TagComponent => "", - _ => throw new ArgumentOutOfRangeException() - }); - } + queryReturnTypeFullName = + $"({ReturnedTupleElementsDuringEnumeration.Select(fieldInfo => fieldInfo.TypeSymbolFullName).SeparateByCommaAndSpace()})"; - builder.AppendLine(" }"); - return builder.ToString(); + return (queryReturnTypeFullName, $"({queryResultConstructorArgs.SeparateByComma()})"); } - StructDeclarationSyntax Generate() - { - var resolvedChunk = - NestedStruct.ResolvedChunk(ReturnedTupleElementsDuringEnumeration, MustReturnEntityDuringIteration, PerformsCollectionChecks).ToArray(); - var typeHandle = - NestedStruct.TypeHandle(ReturnedTupleElementsDuringEnumeration, MustReturnEntityDuringIteration, PerformsCollectionChecks).ToArray(); - - (NestedStruct.Field ResolvedChunkField, NestedStruct.ArgumentInReturnedType TypeHandleArgument)[] pairedFields = - resolvedChunk.Zip(typeHandle, (e1, e2) => - (ResolvedChunkField: e1.Field, TypeHandleField: e2.ArgumentWhenInitializingResolvedChunk)).ToArray(); - - var resultType = - GetResultType(queryResultConstructorArgs: - resolvedChunk - .Where(f => !f.ArgumentInReturnedTupleDuringIndexAccess.IsEmpty) - .Select(f => f.ArgumentInReturnedTupleDuringIndexAccess.Value)); - - string generatedType = - $@" - {TypeCreationHelpers.GeneratedLineTriviaToGeneratedSource} - {(UseBurst ? $"[global::Unity.Burst.NoAlias]{Environment.NewLine}[{BurstCompileAttribute}]" : string.Empty)} - readonly struct {TypeName} - {{ - {(UseBurst ? $"[global::Unity.Burst.NoAlias]{Environment.NewLine}[{BurstCompileAttribute}]" : string.Empty)} - public struct ResolvedChunk - {{ - {resolvedChunk.Select(field => field.Field.Declaration).SeparateByNewLine()} - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public {resultType.FullName} Get(int index) {{ return {resultType.Creation}; }} - }} - - {(UseBurst ? $"[global::Unity.Burst.NoAlias]{Environment.NewLine}[{BurstCompileAttribute}]" : string.Empty)} - public struct TypeHandle - {{ - {"public global::Unity.Entities.EntityManager _entityManager;".EmitIfTrue(typeHandle.Any(f => f.Field.DependsOnEntityManagerField))} - {typeHandle.Select(field => field.Field.Declaration).Distinct().SeparateByNewLine()} - - public TypeHandle(ref global::Unity.Entities.SystemState systemState, bool isReadOnly) - {{ - {"_entityManager = systemState.EntityManager;".EmitIfTrue(typeHandle.Any(f => f.Field.DependsOnEntityManagerField))} - {typeHandle.Select(f => f.Field.AssignmentInNestedStructConstructor).Distinct().SeparateByNewLine()} - }} - - public void Update(ref global::Unity.Entities.SystemState systemState) - {{ - {typeHandle.Select(kvp => kvp.Field.Name) - .Where(name => !string.IsNullOrEmpty(name)) - .Distinct() - .Select(name => $"{name}.Update(ref systemState);").SeparateByNewLine()} - }} - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public ResolvedChunk Resolve(global::Unity.Entities.ArchetypeChunk archetypeChunk) - {{ - var resolvedChunk = new ResolvedChunk(); - {NestedStruct.InitializeResolvedChunkInstanceInTypeHandle(pairedFields).SeparateByNewLine()} - return resolvedChunk; - }} - }} - {GenerateQueryMethod()} - {GenerateEnumerator(resultType.FullName)} - {GenerateCompleteDependenciesMethod()} - }}"; - - return (StructDeclarationSyntax)SyntaxFactory.ParseMemberDeclaration(generatedType); - } + return (ReturnedTupleElementsDuringEnumeration.Single().TypeSymbolFullName, queryResultConstructorArgs.Single()); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachCompilerMessages.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachCompilerMessages.cs deleted file mode 100644 index 18b0c26..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachCompilerMessages.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Microsoft.CodeAnalysis; -using Unity.Entities.SourceGen.SystemGenerator.Common; - -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query -{ - public static class IdiomaticCSharpForEachCompilerMessages - { - private const string ErrorTitle = "ForEachIterationError"; - private const string InfoTitle = "ForEachIterationInfo"; - - public static void SGFE001(SystemDescription systemDescription, Location errorLocation) - { - systemDescription.LogError( - nameof(SGFE001), - ErrorTitle, - "Invocations of `SystemAPI.Query() are currently supported only if they take place inside `foreach` statements.", - errorLocation); - } - - public static void SGFE002(SystemDescription systemDescription, string aspectTypeFullName, string propertyAccessorFullName, Location errorLocation) - { - systemDescription.LogError( - nameof(SGFE002), - ErrorTitle, - $"You attempted to iterate through Query<{aspectTypeFullName}>() in {propertyAccessorFullName}. " + - "Idiomatic `foreach` iteration through queries is only allowed inside methods with a `ref SystemState` parameter in `ISystem` types, OR in all methods in `SystemBase` types.", - errorLocation); - } - - public static void SGFE003(SystemDescription systemDescription, int numChangeFilterTypes, Location errorLocation) - { - systemDescription.LogError( - nameof(SGFE003), - ErrorTitle, - $"You specified {numChangeFilterTypes} change filter types in the same `SystemAPI.Query()` invocation. This is not allowed. " + - "You may specify at most 2 shared component filter types for each `SystemAPI.Query()` invocation.", - errorLocation); - } - - public static void SGFE007(SystemDescription systemDescription, int numSharedComponentFilters, Location errorLocation) - { - systemDescription.LogError( - nameof(SGFE007), - ErrorTitle, - $"You specified {numSharedComponentFilters} shared component filters in the same `SystemAPI.Query()` invocation. This is not allowed. " + - "You may specify at most 2 shared component filter types for each `SystemAPI.Query()` invocation.", - errorLocation); - } - - public static void SGFE008(SystemDescription systemDescription, int numInvocations, Location errorLocation) - { - systemDescription.LogError( - nameof(SGFE008), - ErrorTitle, - $"You invoked `.WithOptions(EntityQueryOptions options)` {numInvocations} times in the same `SystemAPI.Query()` invocation. This is not allowed. " + - "Subsequent calls will override previous options, rather than adding to them. Use the bitwise OR operator '|' to combine multiple options.", - errorLocation); - } - - // Report this as an `Info` message rather than a `Warning` message. If the user's `.rsp` file specifies that all warnings should be treated as errors, - // they won't be able to write SystemAPI.Query()` if we report this as a `Warning`. - public static void SGFE009(SystemDescription systemDescription, string valueTypeComponentFullName, Location errorLocation) - { - systemDescription.LogInfo( - nameof(SGFE009), - InfoTitle, - $"`{valueTypeComponentFullName}` is a value-type and is passed by value. Any modifications made to its data will not be persisted. " + - $"If you wish to modify the data of `{valueTypeComponentFullName}`, use `RefRW<{valueTypeComponentFullName}>()` instead.", - errorLocation); - } - - public static void SGFE010(SystemDescription systemDescription, Location errorLocation) - { - systemDescription.LogError( - nameof(SGFE010), - ErrorTitle, - "The type `T` in `DynamicBuffer`, `UnityEngineComponent`, `RefRO`, `RefRW`, EnabledRefRO and EnabledRefRW must not be generic.", - errorLocation); - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachDescription.cs deleted file mode 100644 index 7e6cf8a..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachDescription.cs +++ /dev/null @@ -1,562 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Operations; -using Unity.Entities.SourceGen.Common; -using Unity.Entities.SourceGen.SystemGenerator.Common; -using QueryDescription = Unity.Entities.SourceGen.SystemGenerator.Common.Query; - -using static Unity.Entities.SourceGen.Common.SourceGenHelpers; - -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query -{ - public class IdiomaticCSharpForEachDescription - { - internal struct QueryData - { - public string TypeSymbolFullName { get; set; } - public ITypeSymbol TypeSymbol { get; set; } - public ITypeSymbol TypeParameterSymbol { get; set; } - public QueryType QueryType { get; set; } - public bool IsGeneric => TypeParameterSymbol != null; - public bool IsReadOnly => QueryType == QueryType.RefRO || - QueryType == QueryType.EnabledRefRO || - QueryType == QueryType.ValueTypeComponent || - QueryType == QueryType.UnmanagedSharedComponent || - QueryType == QueryType.ManagedSharedComponent; - } - - internal (bool IsGenerated, IFEType Value) IFEType { get; } - internal string AspectLookupTypeHandleFieldName { get; set; } - internal IReadOnlyCollection QueryDatas { get; } - internal bool IsBurstEnabled { get; } - internal Location Location { get; } - internal SystemDescription SystemDescription { get; } - internal bool RequiresAspectLookupField { get; } - AttributeData BurstCompileAttribute { get; } - - internal IReadOnlyCollection NoneQueryTypes => _noneQueryTypes; - internal IReadOnlyCollection AnyQueryTypes => _anyQueryTypes; - internal IReadOnlyCollection AllQueryTypes => _allQueryTypes; - internal IReadOnlyCollection DisabledQueryTypes => _disabledQueryTypes; - internal IReadOnlyCollection AbsentQueryTypes => _absentQueryTypes; - internal IReadOnlyCollection ChangeFilterQueryTypes => _changeFilterQueryTypes; - internal IReadOnlyCollection SharedComponentFilterQueryTypes => _sharedComponentFilterQueryTypes; - internal bool HasSharedComponentFilter { get; } - - internal IEnumerable<(SyntaxNode Original, SyntaxNode Replacement)> GetOriginalNodesToReplacementNodes() - { - yield return (Original: _queryCandidate.FullInvocationChainSyntaxNode, Replacement: GetQueryInvocationNodeReplacement()); - - if (_queryCandidate.ContainingStatementNode is ForEachStatementSyntax forEachStatementSyntax) - { - switch (forEachStatementSyntax.Type) - { - // foreach ((RefRW, RefRO) result in SystemAPI.Query, RefRO>()) - case TupleTypeSyntax tupleTypeSyntax: - { - for (int i = 0; i < QueryDatas.Count; i++) - { - var queryData = QueryDatas.ElementAt(i); - var correspondingElementInReturnedTuple = tupleTypeSyntax.Elements[i]; - - switch (queryData.QueryType) - { - case QueryType.RefRW: - case QueryType.RefRW_TagComponent: - yield return (correspondingElementInReturnedTuple.Type, GetUncheckedRefTypeSyntax("UncheckedRefRW", queryData.TypeParameterSymbol.ToFullName())); - break; - case QueryType.RefRO: - case QueryType.RefRO_TagComponent: - yield return (correspondingElementInReturnedTuple.Type, GetUncheckedRefTypeSyntax("UncheckedRefRO", queryData.TypeParameterSymbol.ToFullName())); - break; - } - } - break; - } - case GenericNameSyntax genericNameSyntax: - { - var queryData = QueryDatas.Single(); - var queryType = queryData.QueryType; - - // foreach (RefRW result in SystemAPI.Query>()) - if (genericNameSyntax.Identifier.ValueText != "var") - { - switch (queryType) - { - case QueryType.RefRO_TagComponent: - case QueryType.RefRO: - yield return (genericNameSyntax, GetUncheckedRefTypeSyntax("UncheckedRefRO", queryData.TypeParameterSymbol.ToFullName())); - break; - case QueryType.RefRW_TagComponent: - case QueryType.RefRW: - yield return (genericNameSyntax, GetUncheckedRefTypeSyntax("UncheckedRefRW", queryData.TypeParameterSymbol.ToFullName())); - break; - - } - } - break; - } - } - } - - static SyntaxNode GetUncheckedRefTypeSyntax(string uncheckedRefTypeName, string typeArgName) - { - var genericNameSyntax = - SyntaxFactory.GenericName( - SyntaxFactory.Identifier(uncheckedRefTypeName), - SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(new[] { SyntaxFactory.ParseTypeName(typeArgName) }))); - - return SyntaxFactory.QualifiedName(SyntaxFactory.IdentifierName("Unity.Entities.Internal.InternalCompilerInterface"), genericNameSyntax); - } - } - - public bool Success { get; internal set; } = true; - public string ContainerOrAspectTypeHandleFieldName { get; set; } - public string SourceGeneratedEntityQueryFieldName { get; set; } - - readonly QueryCandidate _queryCandidate; - readonly string _systemStateName; - readonly List _noneQueryTypes = new List(); - readonly List _anyQueryTypes = new List(); - readonly List _allQueryTypes = new List(); - readonly List _disabledQueryTypes = new List(); - readonly List _absentQueryTypes = new List(); - readonly List _changeFilterQueryTypes = new List(); - readonly List _sharedComponentFilterQueryTypes = new List(); - readonly List _sharedComponentFilterArguments = new List(); - readonly List _entityQueryOptionsArguments = new List(); - readonly bool _entityAccessRequired; - readonly string _typeContainingTargetEnumerator_FullyQualifiedName; - - internal EntityQueryOptions GetEntityQueryOptionsArgument() - { - if (!_entityQueryOptionsArguments.Any()) - return EntityQueryOptions.Default; - - var options = EntityQueryOptions.Default; - var argumentExpression = _entityQueryOptionsArguments.First().Expression; - - while (argumentExpression is BinaryExpressionSyntax binaryExpressionSyntax) - { - if (TryParseQualifiedEnumValue(binaryExpressionSyntax.Right.ToString(), out EntityQueryOptions optionArg)) - options |= optionArg; - - argumentExpression = binaryExpressionSyntax.Left; - } - - if (TryParseQualifiedEnumValue(argumentExpression.ToString(), out EntityQueryOptions option)) - options |= option; - - return options; - } - - public IEnumerable GetStatementsToInsertBeforeForEachIteration() - { - // Create a scope wrapping the whole foreach conditioned on there being entities - // to work on, otherwise there is no point in doing any of the logic here. - // Note, we do not close the scope for the if-statement here, it will be closed - // in GetStatementsToInsertAfterForEachIteration. - yield return - @$"if(!{SourceGeneratedEntityQueryFieldName}.IsEmptyIgnoreFilter) - {{ - {_typeContainingTargetEnumerator_FullyQualifiedName}.CompleteDependencyBeforeRW(ref {_systemStateName}); - "; - - yield return $"__TypeHandle.{ContainerOrAspectTypeHandleFieldName}.Update(ref {_systemStateName});"; - - if (RequiresAspectLookupField) - yield return $"__TypeHandle.{AspectLookupTypeHandleFieldName}.Update(ref {_systemStateName});"; - - - foreach (var arg in _sharedComponentFilterArguments) - yield return $"{SourceGeneratedEntityQueryFieldName}.SetSharedComponentFilter({arg});"; - } - - private InvocationExpressionSyntax GetQueryInvocationNodeReplacement() - { - var memberAccessExpressionSyntax = - SyntaxFactory.MemberAccessExpression( - SyntaxKind.SimpleMemberAccessExpression, - SyntaxFactory.IdentifierName(_typeContainingTargetEnumerator_FullyQualifiedName), - SyntaxFactory.IdentifierName("Query")); - - return SyntaxFactory.InvocationExpression(memberAccessExpressionSyntax, SyntaxFactory.ArgumentList(GetArgumentsForStaticQueryMethod())); - - SeparatedSyntaxList GetArgumentsForStaticQueryMethod() - { - var entityQueryArg = SyntaxFactory.Argument(SyntaxFactory.IdentifierName(SourceGeneratedEntityQueryFieldName)); - // argument being e.g. `__TypeHandle.IFE_2490249,` - var containerOrAspectTypeHandleArg = SyntaxFactory.Argument( - SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("__TypeHandle"), SyntaxFactory.IdentifierName(ContainerOrAspectTypeHandleFieldName))); - return SyntaxFactory.SeparatedList(new[] { entityQueryArg, containerOrAspectTypeHandleArg }); - } - } - - public IdiomaticCSharpForEachDescription(SystemDescription systemDescription, QueryCandidate queryCandidate, int numForEachsPreviouslySeenInSystem) - { - if (systemDescription.SemanticModel.GetOperation(queryCandidate.FullInvocationChainSyntaxNode) is IInvocationOperation invocationOperation - && IsSystemAPIQueryInvocation(invocationOperation)) - { - _queryCandidate = queryCandidate; - - SystemDescription = systemDescription; - Location = queryCandidate.FullInvocationChainSyntaxNode.GetLocation(); - - if (TryGetQueryDatas(out var queryDatas)) - QueryDatas = queryDatas.ToArray(); - else - { - Success = false; - return; - } - - var containingMethod = queryCandidate.FullInvocationChainSyntaxNode.AncestorOfKindOrDefault(); - if (containingMethod != null) - { - var methodSymbol = SystemDescription.SemanticModel.GetDeclaredSymbol(containingMethod); - - foreach (var node in queryCandidate.MethodInvocationNodes) - { - switch (node.Expression) - { - case MemberAccessExpressionSyntax { Name: GenericNameSyntax genericNameSyntax }: - { - switch (genericNameSyntax.Identifier.ValueText) - { - case "WithDisabled": - _disabledQueryTypes.AddRange( - genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => - new QueryDescription - { - TypeSymbol = (ITypeSymbol)systemDescription.SemanticModel.GetSymbolInfo(typeArg).Symbol, - Type = SystemGenerator.Common.QueryType.Disabled, - IsReadOnly = true - })); - break; - case "WithAbsent": - _absentQueryTypes.AddRange( - genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => - new QueryDescription - { - TypeSymbol = (ITypeSymbol)systemDescription.SemanticModel.GetSymbolInfo(typeArg).Symbol, - Type = SystemGenerator.Common.QueryType.Absent, - IsReadOnly = true - })); - break; - case "WithAll": - _allQueryTypes.AddRange( - genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => - new QueryDescription - { - TypeSymbol = (ITypeSymbol)systemDescription.SemanticModel.GetSymbolInfo(typeArg).Symbol, - Type = SystemGenerator.Common.QueryType.All, - IsReadOnly = true - })); - break; - case "WithAny": - _anyQueryTypes.AddRange( - genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => - new QueryDescription - { - TypeSymbol = (ITypeSymbol)systemDescription.SemanticModel.GetSymbolInfo(typeArg).Symbol, - Type = SystemGenerator.Common.QueryType.Any, - IsReadOnly = true - })); - break; - case "WithNone": - _noneQueryTypes.AddRange( - genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => - new QueryDescription - { - TypeSymbol = (ITypeSymbol)systemDescription.SemanticModel.GetSymbolInfo(typeArg).Symbol, - Type = SystemGenerator.Common.QueryType.None, - IsReadOnly = true - })); - break; - case "WithChangeFilter": - _changeFilterQueryTypes.AddRange( - genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => - new QueryDescription - { - TypeSymbol = (ITypeSymbol)systemDescription.SemanticModel.GetSymbolInfo(typeArg).Symbol, - Type = SystemGenerator.Common.QueryType.ChangeFilter, - IsReadOnly = true - })); - break; - case "WithSharedComponentFilter": - _sharedComponentFilterQueryTypes.AddRange( - genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => - new QueryDescription - { - TypeSymbol = (ITypeSymbol)systemDescription.SemanticModel.GetSymbolInfo(typeArg).Symbol, - Type = SystemGenerator.Common.QueryType.All, - IsReadOnly = true - })); - _sharedComponentFilterArguments.AddRange(node.ArgumentList.Arguments); - HasSharedComponentFilter = true; - break; - } - break; - } - case MemberAccessExpressionSyntax { Name: IdentifierNameSyntax identifierNameSyntax }: - { - switch (identifierNameSyntax.Identifier.ValueText) - { - case "WithSharedComponentFilter": - _sharedComponentFilterQueryTypes.AddRange( - node.ArgumentList.Arguments.Select(arg => - new QueryDescription - { - TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(arg.Expression).Type, - Type = SystemGenerator.Common.QueryType.All, - IsReadOnly = true - })); - _sharedComponentFilterArguments.AddRange(node.ArgumentList.Arguments); - HasSharedComponentFilter = true; - break; - case "WithOptions": - _entityQueryOptionsArguments.Add(node.ArgumentList.Arguments.Single()); - break; - case "WithEntityAccess": - _entityAccessRequired = true; - break; - } - break; - } - } - } - if (!_queryCandidate.IsContainedInForEachStatement) - { - IdiomaticCSharpForEachCompilerMessages.SGFE001(SystemDescription, Location); - Success = false; - return; - } - - if (_changeFilterQueryTypes.Count > 2) - IdiomaticCSharpForEachCompilerMessages.SGFE003(SystemDescription, _changeFilterQueryTypes.Count, Location); - if (_sharedComponentFilterQueryTypes.Count > 2) - IdiomaticCSharpForEachCompilerMessages.SGFE007(SystemDescription, _sharedComponentFilterQueryTypes.Count, Location); - if (_entityQueryOptionsArguments.Count > 1) - IdiomaticCSharpForEachCompilerMessages.SGFE008(SystemDescription, _entityQueryOptionsArguments.Count, Location); - - if (SystemDescription.TryGetSystemStateParameterName(_queryCandidate, out var systemStateExpression)) - _systemStateName = systemStateExpression.ToFullString(); - else - { - Success = false; - return; - } - - var mustGenerateContainerType = MustGenerateContainerType(QueryDatas, _entityAccessRequired); - - bool useAspect = !mustGenerateContainerType; - // Aspects always have nested `Lookup`s, which must be updated whenever there is a system update - RequiresAspectLookupField = useAspect; - - var burstCompileAttribute = methodSymbol.GetAttributes().SingleOrDefault(a => a.AttributeClass.ToFullName() == "Unity.Burst.BurstCompileAttribute"); - IsBurstEnabled = burstCompileAttribute != null; - BurstCompileAttribute = burstCompileAttribute; - - if (mustGenerateContainerType) - { - string ifeTypeName = $"IFE_{systemDescription.HandlesDescription.UniqueId}_{numForEachsPreviouslySeenInSystem}"; - - var ifeType = new IFEType - { - PerformsCollectionChecks = SystemDescription.IsUnityCollectionChecksEnabled, - MustReturnEntityDuringIteration = _entityAccessRequired, - TypeName = ifeTypeName, - BurstCompileAttribute = BurstCompileAttribute, - FullyQualifiedTypeName = - queryCandidate - .FullInvocationChainSyntaxNode - .GetContainingTypesAndNamespacesFromMostToLeastNested() - .Select(GetIdentifier) - .Reverse() - .Append(ifeTypeName) - .SeparateByDot(), - ReturnedTupleElementsDuringEnumeration = - QueryDatas - .Select((queryData, index) => - { - string typeArgumentFullName; - switch (queryData.QueryType) - { - case QueryType.RefRO: - typeArgumentFullName = queryData.TypeParameterSymbol.ToFullName(); - return new ReturnedTupleElementDuringEnumeration( - $"Unity.Entities.Internal.InternalCompilerInterface.UncheckedRefRO<{typeArgumentFullName}>", - typeArgumentFullName: typeArgumentFullName, - elementName: $"item{index + 1}", - type: queryData.QueryType); - case QueryType.RefRW: - typeArgumentFullName = queryData.TypeParameterSymbol.ToFullName(); - return new ReturnedTupleElementDuringEnumeration( - $"Unity.Entities.Internal.InternalCompilerInterface.UncheckedRefRW<{typeArgumentFullName}>", - typeArgumentFullName: typeArgumentFullName, - elementName: $"item{index + 1}", - type: queryData.QueryType); - default: - typeArgumentFullName = queryData.TypeParameterSymbol is { } symbol ? symbol.ToFullName() : string.Empty; - return new ReturnedTupleElementDuringEnumeration( - queryData.TypeSymbolFullName, - typeArgumentFullName: typeArgumentFullName, - elementName: $"item{index + 1}", - type: queryData.QueryType); - } - }) - .ToArray() - }; - - IFEType = (IsGenerated: true, ifeType); - SourceGeneratedEntityQueryFieldName = $"{IFEType.Value.TypeName}_Query"; - - _typeContainingTargetEnumerator_FullyQualifiedName = ifeType.FullyQualifiedTypeName; - } - else - _typeContainingTargetEnumerator_FullyQualifiedName = QueryDatas.Single().TypeSymbolFullName; // Use `Aspect` enumerator - } - else - { - var propertyDeclarationSyntax = queryCandidate.FullInvocationChainSyntaxNode.AncestorOfKind(); - var propertySymbol = ModelExtensions.GetDeclaredSymbol(systemDescription.SemanticModel, propertyDeclarationSyntax); - - IdiomaticCSharpForEachCompilerMessages.SGFE002( - systemDescription, - systemDescription.SystemTypeSymbol.ToFullName(), - propertySymbol.OriginalDefinition.ToString(), - queryCandidate.ContainingTypeNode.GetLocation()); - Success = false; - } - } - else - Success = false; - - static bool IsSystemAPIQueryInvocation(IInvocationOperation operation) - { - var constructedFrom = operation.TargetMethod.ConstructedFrom.ToString(); - if (constructedFrom.StartsWith("Unity.Entities.QueryEnumerable<")) - return true; - if (constructedFrom.StartsWith("Unity.Entities.QueryEnumerableWithEntity<")) - return true; - return constructedFrom.StartsWith("Unity.Entities.SystemAPI.Query<"); - } - - // Returns true if we are querying only a single aspect without `.WithEntityAccess()` -- - // we don't need a container type in that case; we can just use the existing aspect type along with - // its generated enumerators, nested `ResolvedChunk`, etc. - static bool MustGenerateContainerType(IReadOnlyCollection queryDatas, bool entityAccessRequired) - { - if (queryDatas.Count > 1) - return true; - if (entityAccessRequired) - return true; - return queryDatas.Single().QueryType != QueryType.Aspect; - } - - static string GetIdentifier(MemberDeclarationSyntax memberDeclarationSyntax) - { - switch (memberDeclarationSyntax) - { - case ClassDeclarationSyntax classDeclarationSyntax: - return classDeclarationSyntax.Identifier.ValueText; - case StructDeclarationSyntax structDeclarationSyntax: - return structDeclarationSyntax.Identifier.ValueText; - case NamespaceDeclarationSyntax namespaceDeclarationSyntax: - var identifierName = namespaceDeclarationSyntax.ChildNodes().OfType().FirstOrDefault(); - return - identifierName != null - ? identifierName.Identifier.ValueText - : namespaceDeclarationSyntax.ChildNodes().OfType().First().ToString(); - default: - throw new ArgumentOutOfRangeException(); - } - } - } - - private bool TryGetQueryDatas(out ICollection queryTypes) - { - queryTypes = new List(); - - foreach (var typeSyntax in _queryCandidate.QueryTypeNodes) - { - var typeSymbol = SystemDescription.SemanticModel.GetTypeInfo(typeSyntax).Type; - var typeParameterSymbol = default(ITypeSymbol); - - var genericNameCandidate = typeSyntax; - if (typeSyntax is QualifiedNameSyntax qualifiedNameSyntax) // This is the case when people type out their syntax Query - genericNameCandidate = qualifiedNameSyntax.Right; - if (genericNameCandidate is GenericNameSyntax genericNameSyntax) - typeParameterSymbol = SystemDescription.SemanticModel.GetTypeInfo(genericNameSyntax.TypeArgumentList.Arguments.Single()).Type; - - var result = TryGetIdiomaticCSharpForEachQueryType(typeSymbol, typeSyntax.GetLocation()); - - if (!result.Success) - return false; - - if (result.Value == QueryType.ValueTypeComponent) - IdiomaticCSharpForEachCompilerMessages.SGFE009(SystemDescription, typeSymbol.ToFullName(), Location); - - queryTypes.Add(new QueryData - { - TypeParameterSymbol = typeParameterSymbol, - TypeSymbol = typeSymbol, - TypeSymbolFullName = typeSymbol.ToFullName(), - QueryType = result.Value - }); - } - return true; - - (bool Success, QueryType Value) TryGetIdiomaticCSharpForEachQueryType(ITypeSymbol typeSymbol, Location errorLocation) - { - if (typeSymbol.IsAspect()) - return (true, QueryType.Aspect); - - if (typeSymbol.IsSharedComponent()) - return (true, typeSymbol.IsUnmanagedType ? QueryType.UnmanagedSharedComponent : QueryType.ManagedSharedComponent); - - if (typeSymbol.IsComponent()) - { - if (typeSymbol.InheritsFromType("System.ValueType")) - return - (true, typeSymbol.IsZeroSizedComponent() - ? QueryType.TagComponent - : QueryType.ValueTypeComponent); - return (true, QueryType.ManagedComponent); - } - - var typeArgument = ((INamedTypeSymbol)typeSymbol).TypeArguments[0]; - if (typeArgument is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.Arity != 0) - { - IdiomaticCSharpForEachCompilerMessages.SGFE010(SystemDescription, errorLocation); - return (false, QueryType.Invalid); - } - - return typeSymbol.Name switch - { - "DynamicBuffer" => (true, QueryType.DynamicBuffer), - "RefRW" => (true, typeArgument.IsZeroSizedComponent() ? QueryType.RefRW_TagComponent : QueryType.RefRW), - "RefRO" => (true, typeArgument.IsZeroSizedComponent() ? QueryType.RefRO_TagComponent : QueryType.RefRO), - "EnabledRefRW" => (true, QueryType.EnabledRefRW), - "EnabledRefRO" => (true, QueryType.EnabledRefRO), - "UnityEngineComponent" => (true, QueryType.UnityEngineComponent), - _ => throw new ArgumentOutOfRangeException() - }; - } - } - - public IEnumerable GetStatementsToInsertAfterForEachIteration() - { - if (HasSharedComponentFilter) - yield return $"{SourceGeneratedEntityQueryFieldName}.ResetFilter();"; - - // End scope started in GetStatementsToInsertBeforeForEachIteration for the - // if(!.IsEmptyIgnoreFilter) { block - yield return $"}}"; - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachModule.Rewriter.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachModule.Rewriter.cs deleted file mode 100644 index 6cbeb5b..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachModule.Rewriter.cs +++ /dev/null @@ -1,152 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.Common; -using Unity.Entities.SourceGen.SystemGenerator.Common; - -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query -{ - public partial class IdiomaticCSharpForEachModule - { - class SystemAPIQueryRewriter : SystemRewriter - { - struct RewrittenSystemAPIQueryInvocation - { - public SyntaxNode ReplacementNode; - public IdiomaticCSharpForEachDescription Description; - public int OriginalForEachStatementLineNumber; - } - - readonly Dictionary _rewrittenNodes; - readonly Dictionary _descriptionsToReplacementLookups; - readonly IReadOnlyCollection _descriptions; - readonly Dictionary statementSnippets, int originalLineNumber)> _targetAndStatementsToInsertBefore; - readonly Dictionary> _targetAndStatementsToInsertAfter; - bool _replacedNode; - - public override IEnumerable NodesToTrack => - _descriptionsToReplacementLookups.SelectMany(kvp => kvp.Value).Select(kvp => kvp.Original); - - public SystemAPIQueryRewriter(IReadOnlyCollection descriptionsInSameSystemType) - { - _descriptions = descriptionsInSameSystemType; - _targetAndStatementsToInsertBefore = new Dictionary, int)>(); - _targetAndStatementsToInsertAfter = new Dictionary>(); - _rewrittenNodes = new Dictionary(); - _descriptionsToReplacementLookups = - descriptionsInSameSystemType.ToDictionary(d => d, d => d.GetOriginalNodesToReplacementNodes().ToArray()); - } - - // By the time this method is invoked, the system has already been rewritten at least once. - // In other words, the `systemRootNode` argument passed to this method is the root node of the REWRITTEN system -- - // i.e., a copy of the original system with changes applied. - public override SyntaxNode VisitTrackedSystem(SyntaxNode systemRootNode, string originalFilePath) - { - m_OriginalFilePath = originalFilePath; - - foreach (var description in _descriptions) - { - var replacements = _descriptionsToReplacementLookups[description]; - - var originalNodes = replacements.Select(kvp => kvp.Original).ToArray(); - var replacementNodes = replacements.Select(kvp => kvp.Replacement).ToArray(); - - var nodesRewrittenDuringTracking = (systemRootNode.GetCurrentNodes(originalNodes) ?? originalNodes).ToArray(); - - for (int i = 0; i < nodesRewrittenDuringTracking.Length; i++) - { - _rewrittenNodes.Add( - nodesRewrittenDuringTracking[i], - new RewrittenSystemAPIQueryInvocation - { - Description = description, - OriginalForEachStatementLineNumber = originalNodes[i].GetLineNumber(), - ReplacementNode = replacementNodes[i] - }); - } - } - return Visit(systemRootNode); - } - - public override SyntaxNode Visit(SyntaxNode syntaxNode) - { - if (syntaxNode == null) - return null; - - var replacedNodeAndChildren = base.Visit(syntaxNode); - - // If the current node is a node we want to replace -- e.g. `SystemAPI.Query()` - if (_rewrittenNodes.TryGetValue(syntaxNode, out var rewrittenData)) - { - // Replace the current node - replacedNodeAndChildren = rewrittenData.ReplacementNode; - _replacedNode = true; - - var containingForEachStatement = syntaxNode.Ancestors().OfType().First(); - - if (!_targetAndStatementsToInsertBefore.ContainsKey(containingForEachStatement)) - _targetAndStatementsToInsertBefore[containingForEachStatement] = (new HashSet(), rewrittenData.OriginalForEachStatementLineNumber); - - foreach (var newStatement in rewrittenData.Description.GetStatementsToInsertBeforeForEachIteration()) - _targetAndStatementsToInsertBefore[containingForEachStatement].statementSnippets.Add(newStatement); - - var statementsToInsertAfterForEachIteration = rewrittenData.Description.GetStatementsToInsertAfterForEachIteration().ToArray(); - if (statementsToInsertAfterForEachIteration.Length > 0) - { - if (!_targetAndStatementsToInsertAfter.ContainsKey(containingForEachStatement)) - _targetAndStatementsToInsertAfter[containingForEachStatement.Statement] = new HashSet(); - - foreach (var newStatement in statementsToInsertAfterForEachIteration) - _targetAndStatementsToInsertAfter[containingForEachStatement.Statement].Add(newStatement); - } - } - - // If we want to insert additional statements - if (replacedNodeAndChildren is StatementSyntax statementSyntax) - { - if (_targetAndStatementsToInsertBefore.TryGetValue(syntaxNode, out (HashSet statementSnippets, int originalLineNumber) statementsToInsertBefore)) - { - var statements = - new List( - statementsToInsertBefore.statementSnippets.Select(s => - (StatementSyntax)SyntaxFactory.ParseStatement(s).WithHiddenLineTrivia())) - { - (StatementSyntax)statementSyntax.WithHiddenLineTrivia() - }; - statements[0] = statements[0].WithLineTrivia(m_OriginalFilePath, statementsToInsertBefore.originalLineNumber) as StatementSyntax; - - replacedNodeAndChildren = - SyntaxFactory.Block( - new SyntaxList(), - SyntaxFactory.MissingToken(SyntaxKind.OpenBraceToken), - new SyntaxList(statements), - SyntaxFactory.MissingToken(SyntaxKind.CloseBraceToken)); - } - - else if (_targetAndStatementsToInsertAfter.TryGetValue(syntaxNode, out HashSet statementsToInsertAfter)) - { - var statements = new List { statementSyntax.WithoutTrivia() }; - statements.AddRange(statementsToInsertAfter.Select(s => (StatementSyntax)SyntaxFactory.ParseStatement(s).WithHiddenLineTrivia())); - - replacedNodeAndChildren = - SyntaxFactory.Block( - new SyntaxList(), - SyntaxFactory.MissingToken(SyntaxKind.OpenBraceToken), - new SyntaxList(statements), - SyntaxFactory.MissingToken(SyntaxKind.CloseBraceToken)); - } - } - - // If we have performed any replacements, we need to update the `RewrittenMemberHashCodeToSyntaxNode` dictionary accordingly - if (replacedNodeAndChildren is MemberDeclarationSyntax memberDeclarationSyntax && _replacedNode) - { - RecordChangedMember(memberDeclarationSyntax); - _replacedNode = false; - } - return replacedNodeAndChildren; - } - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachModule.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachModule.cs deleted file mode 100644 index ddd8e0c..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IdiomaticCSharpForEachModule.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.SystemGenerator.Common; -using static Unity.Entities.SourceGen.Common.QueryVerification; - -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query -{ - public partial class IdiomaticCSharpForEachModule : ISystemModule - { - private readonly List _queryCandidates = new List(); - private Dictionary _candidatesGroupedByContainingSystemTypes; - - private Dictionary CandidatesGroupedByContainingSystemTypes - { - get - { - if (_candidatesGroupedByContainingSystemTypes == null) - { - _candidatesGroupedByContainingSystemTypes = - _queryCandidates - .GroupBy(c => c.ContainingTypeNode) - .ToDictionary(group => group.Key, group => group.ToArray()); - } - return _candidatesGroupedByContainingSystemTypes; - } - } - - public IEnumerable<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)> Candidates - => _queryCandidates.Select(candidate => (SyntaxNode: candidate.FullInvocationChainSyntaxNode, ContainingType: candidate.ContainingTypeNode)); - - public bool RequiresReferenceToBurst { get; private set; } - - public void OnReceiveSyntaxNode(SyntaxNode node) - { - if (node is InvocationExpressionSyntax invocationExpressionSyntax) - { - switch (invocationExpressionSyntax.Expression) - { - case MemberAccessExpressionSyntax memberAccessExpressionSyntax: - { - switch (memberAccessExpressionSyntax.Name) - { - case GenericNameSyntax { Identifier: { ValueText: "Query" } } genericNameSyntax: - { - var candidate = QueryCandidate.From(invocationExpressionSyntax, genericNameSyntax.TypeArgumentList.Arguments); - _queryCandidates.Add(candidate); - break; - } - } - break; - } - case GenericNameSyntax { Identifier: { ValueText: "Query" } } genericNameSyntax: - { - var candidate = QueryCandidate.From(invocationExpressionSyntax, genericNameSyntax.TypeArgumentList.Arguments); - _queryCandidates.Add(candidate); - break; - } - } - } - } - - public bool RegisterChangesInSystem(SystemDescription systemDescription) - { - var idiomaticCSharpForEachDescriptions = new List(); - foreach (var queryCandidate in CandidatesGroupedByContainingSystemTypes[systemDescription.SystemTypeSyntax]) - { - var description = new IdiomaticCSharpForEachDescription(systemDescription, queryCandidate, idiomaticCSharpForEachDescriptions.Count); - - description.Success &= - VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.AllQueryTypes, invokedMethodName: "WithAll"); - description.Success &= - VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.NoneQueryTypes, invokedMethodName: "WithNone"); - description.Success &= - VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.AnyQueryTypes, invokedMethodName: "WithAny"); - - description.Success &= - VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.AbsentQueryTypes, description.DisabledQueryTypes, "WithAbsent", "WithDisabled"); - description.Success &= - VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.AbsentQueryTypes, description.AllQueryTypes, "WithAbsent", "WithAll"); - description.Success &= - VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.AbsentQueryTypes, description.AnyQueryTypes, "WithAbsent", "WithAny"); - description.Success &= - VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.NoneQueryTypes, description.AllQueryTypes, "WithNone", "WithAll"); - description.Success &= - VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.NoneQueryTypes, description.AnyQueryTypes, "WithNone", "WithAny"); - description.Success &= - VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.AnyQueryTypes, description.AllQueryTypes, "WithAny", "WithAll"); - - if (!description.Success) // if !description.Success, TypeSymbolsForEntityQueryCreation might not be right, thus causing an exception - continue; - - var queriedTypes = - description.QueryDatas.Select(data => new Common.Query - { - TypeSymbol = data.TypeSymbol, - Type = Common.QueryType.All, - IsReadOnly = data.IsReadOnly - }).ToArray(); - - description.Success &= VerifyNoMutuallyExclusiveQueries( - description.SystemDescription, - description.Location, - description.AbsentQueryTypes, - queriedTypes, - "WithAbsent", - "main queried Aspect type", - compareTypeSymbolsOnly: true); - - description.Success &= VerifyNoMutuallyExclusiveQueries( - description.SystemDescription, - description.Location, - description.AbsentQueryTypes, - queriedTypes, - "WithDisabled", - "main queried Aspect type", - compareTypeSymbolsOnly: true); - - description.Success &= VerifyNoMutuallyExclusiveQueries( - description.SystemDescription, - description.Location, - description.NoneQueryTypes, - queriedTypes, - "WithNone", - "main queried Aspect type", - compareTypeSymbolsOnly: true); - - description.Success &= VerifyNoMutuallyExclusiveQueries( - description.SystemDescription, - description.Location, - description.AnyQueryTypes, - queriedTypes, - "WithAny", - "main queried Aspect type", - compareTypeSymbolsOnly: true); - - if (description.Success) - { - if (description.IsBurstEnabled) - RequiresReferenceToBurst = true; - idiomaticCSharpForEachDescriptions.Add(description); - } - } - foreach (var description in idiomaticCSharpForEachDescriptions) - { - if (description.IFEType.IsGenerated) - { - systemDescription.NewMiscellaneousMembers.Add(description.IFEType.Value.StructDeclarationSyntax); - - description.ContainerOrAspectTypeHandleFieldName = - systemDescription.HandlesDescription.GetOrCreateSourceGeneratedTypeHandleField(description.IFEType.Value.FullyQualifiedTypeName); - } - else - { - // We do not generate container types when querying a single Aspect without `.WithEntityAccess()` - description.ContainerOrAspectTypeHandleFieldName = - systemDescription.HandlesDescription.GetOrCreateTypeHandleField(description.QueryDatas.Single().TypeSymbol, isReadOnly: false); - - if (description.RequiresAspectLookupField) - { - description.AspectLookupTypeHandleFieldName = systemDescription.HandlesDescription.GetOrCreateAspectLookup(description.QueryDatas.Single().TypeSymbol, isReadOnly: false); - } - } - description.SourceGeneratedEntityQueryFieldName = - systemDescription.HandlesDescription.GetOrCreateQueryField( - new SingleArchetypeQueryFieldDescription( - new Archetype( - description.AllQueryTypes - .Concat(description.SharedComponentFilterQueryTypes) - .Concat(description.QueryDatas.Select(queryData => - new Common.Query - { - IsReadOnly = queryData.IsReadOnly, - TypeSymbol = queryData.IsGeneric - ? queryData.TypeParameterSymbol - : queryData.TypeSymbol, - Type = Common.QueryType.All - })) - .ToArray(), - description.AnyQueryTypes, - description.NoneQueryTypes, - description.DisabledQueryTypes, - description.AbsentQueryTypes, - description.GetEntityQueryOptionsArgument()), - description.ChangeFilterQueryTypes)); - } - - systemDescription.Rewriters.Add(new SystemAPIQueryRewriter(idiomaticCSharpForEachDescriptions)); - return true; - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeCompilerMessages.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeCompilerMessages.cs new file mode 100644 index 0000000..8ecd56b --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeCompilerMessages.cs @@ -0,0 +1,97 @@ +using Microsoft.CodeAnalysis; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +public static class IfeCompilerMessages +{ + private const string ErrorTitle = "ForEachIterationError"; + private const string InfoTitle = "ForEachIterationInfo"; + + public static void SGFE001(SystemDescription systemDescription, Location errorLocation) + { + systemDescription.LogError( + nameof(SGFE001), + ErrorTitle, + "Invocations of `SystemAPI.Query() are currently supported only if they take place inside `foreach` statements.", + errorLocation); + } + + public static void SGFE002(SystemDescription systemDescription, string propertyAccessorFullName, Location errorLocation) + { + systemDescription.LogError( + nameof(SGFE002), + ErrorTitle, + $"You attempted to iterate through the result of an `SystemAPI.Query` invocation in the {propertyAccessorFullName} property. " + + " Such iterations are only allowed inside methods with a `ref SystemState` parameter in `ISystem` types, OR in all methods in `SystemBase` types.", + errorLocation); + } + + public static void SGFE003(SystemDescription systemDescription, int numChangeFilterTypes, Location errorLocation) + { + systemDescription.LogError( + nameof(SGFE003), + ErrorTitle, + $"You specified {numChangeFilterTypes} change filter types in the same `SystemAPI.Query()` invocation. This is not allowed. " + + "You may specify at most 2 shared component filter types for each `SystemAPI.Query()` invocation.", + errorLocation); + } + + public static void SGFE007(SystemDescription systemDescription, int numSharedComponentFilters, Location errorLocation) + { + systemDescription.LogError( + nameof(SGFE007), + ErrorTitle, + $"You specified {numSharedComponentFilters} shared component filters in the same `SystemAPI.Query()` invocation. This is not allowed. " + + "You may specify at most 2 shared component filter types for each `SystemAPI.Query()` invocation.", + errorLocation); + } + + public static void SGFE008(SystemDescription systemDescription, int numInvocations, Location errorLocation) + { + systemDescription.LogError( + nameof(SGFE008), + ErrorTitle, + $"You invoked `.WithOptions(EntityQueryOptions options)` {numInvocations} times in the same `SystemAPI.Query()` invocation. This is not allowed. " + + "Subsequent calls will override previous options, rather than adding to them. Use the bitwise OR operator '|' to combine multiple options.", + errorLocation); + } + + // Report this as an `Info` message rather than a `Warning` message. If the user's `.rsp` file specifies that all warnings should be treated as errors, + // they won't be able to write SystemAPI.Query()` if we report this as a `Warning`. + public static void SGFE009(SystemDescription systemDescription, string valueTypeComponentFullName, Location errorLocation) + { + systemDescription.LogInfo( + nameof(SGFE009), + InfoTitle, + $"`{valueTypeComponentFullName}` is a value-type and is passed by value. Any modifications made to its data will not be persisted. " + + $"If you wish to modify the data of `{valueTypeComponentFullName}`, use `RefRW<{valueTypeComponentFullName}>()` instead.", + errorLocation); + } + + public static void SGFE010(SystemDescription systemDescription, Location errorLocation) + { + systemDescription.LogError( + nameof(SGFE010), + ErrorTitle, + "The type `T` in `DynamicBuffer`, `UnityEngineComponent`, `RefRO`, `RefRW`, EnabledRefRO and EnabledRefRW must not contain a generic type parameter.", + errorLocation); + } + public static void SGFE011(SystemDescription systemDescription, Location errorLocation) + { + systemDescription.LogError( + nameof(SGFE011), + ErrorTitle, + "The type `T` in `DynamicBuffer`, `UnityEngineComponent`, `RefRO`, `RefRW`, EnabledRefRO and EnabledRefRW can not be a generic Type parameter.", + errorLocation); + } + public static void SGFE012(SystemDescription systemDescription, string typeArgFullName, Location errorLocation) + { + systemDescription.LogError( + nameof(SGFE012), + ErrorTitle, + $"Invoking `SystemAPI.Query<{typeArgFullName}>().WithAbsent<{typeArgFullName}>() is not allowed. You cannot ask for a reference to an explicitly excluded component.", + errorLocation); + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeDescription.QueryData.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeDescription.QueryData.cs new file mode 100644 index 0000000..68fb108 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeDescription.QueryData.cs @@ -0,0 +1,150 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +public partial class IfeDescription +{ + struct QueryData + { + public string TypeSymbolFullName { get; set; } + public ITypeSymbol TypeSymbol { get; set; } + public ITypeSymbol TypeParameterSymbol { get; set; } + public QueryType QueryType { get; set; } + public bool IsReadOnly => QueryType is QueryType.RefRO or QueryType.EnabledRefRO or QueryType.ValueTypeComponent or QueryType.UnmanagedSharedComponent or QueryType.ManagedSharedComponent; + public ITypeSymbol QueriedTypeSymbol => TypeParameterSymbol ?? TypeSymbol; + } + + private bool TryGetQueryDatas() + { +#pragma warning disable RS1024 + InitialIterableEnableableTypeSymbols = new HashSet(); +#pragma warning restore RS1024 + + InitialIterableEnableableQueryDatas = new List(); + IterableEnableableQueryDatasToBeTreatedAsAllComponents = new List(); + + AllIterableQueryDatas = new List(); + + foreach (var typeSyntax in QueryCandidate.QueryTypeNodes) + { + var typeSymbol = SystemDescription.SemanticModel.GetTypeInfo(typeSyntax).Type; + var typeParameterSymbol = default(ITypeSymbol); + + var genericNameCandidate = typeSyntax; + if (typeSyntax is QualifiedNameSyntax qualifiedNameSyntax) // This is the case when people type out their syntax Query + genericNameCandidate = qualifiedNameSyntax.Right; + if (genericNameCandidate is GenericNameSyntax genericNameSyntax) + { + var typeArg = genericNameSyntax.TypeArgumentList.Arguments.Single(); + typeParameterSymbol = SystemDescription.SemanticModel.GetTypeInfo(typeArg).Type; + } + + var result = TryGetIdiomaticCSharpForEachQueryType(typeSymbol, typeSyntax.GetLocation()); + + if (result.QueryType == QueryType.Invalid) + return false; + + if (result.QueryType == QueryType.ValueTypeComponent) + IfeCompilerMessages.SGFE009(SystemDescription, typeSymbol.ToFullName(), Location); + + var queryData = new QueryData + { + TypeParameterSymbol = typeParameterSymbol, + TypeSymbol = typeSymbol, + TypeSymbolFullName = typeSymbol.ToFullName(), + QueryType = result.QueryType, + }; + if (result.IsTypeEnableable) + { + InitialIterableEnableableQueryDatas.Add(queryData); + IterableEnableableQueryDatasToBeTreatedAsAllComponents.Add(queryData); + + InitialIterableEnableableTypeSymbols.Add(queryData.QueriedTypeSymbol); + + AllIterableQueryDatas.Add(queryData); + } + else + { + AllIterableQueryDatas.Add(queryData); + + _iterableNonEnableableTypes.Add(new Common.Query() + { + IsReadOnly = queryData.IsReadOnly, + TypeSymbol = queryData.QueriedTypeSymbol, + Type = Common.QueryType.All + }); + } + } + return true; + + static bool HasTypeParameter(ITypeSymbol typeArgument) + { + if (typeArgument is INamedTypeSymbol namedTypeSymbol) + foreach (var typeArg in namedTypeSymbol.TypeArguments) + if (typeArg is ITypeParameterSymbol || HasTypeParameter(typeArg)) + return true; + + return false; + } + + (QueryType QueryType, bool IsTypeEnableable) TryGetIdiomaticCSharpForEachQueryType(ITypeSymbol typeSymbol, Location errorLocation) + { + // `typeSymbol` is an error type. This is usually caused by an ambiguous type. + // Go ahead and mark the query as invalid and let roslyn report the other error. + if (typeSymbol is IErrorTypeSymbol) + return (QueryType.Invalid, false); + + if (typeSymbol.IsAspect()) + return (QueryType.Aspect, false); + + if (typeSymbol.IsSharedComponent()) + return (typeSymbol.IsUnmanagedType ? QueryType.UnmanagedSharedComponent : QueryType.ManagedSharedComponent, false); + + if (typeSymbol.IsComponent()) + { + if (typeSymbol.InheritsFromType("System.ValueType")) + return (typeSymbol.IsZeroSizedComponent() ? QueryType.TagComponent : QueryType.ValueTypeComponent, typeSymbol.IsEnableableComponent()); + + return(QueryType.ManagedComponent, false); + } + + var typeArgument = ((INamedTypeSymbol)typeSymbol).TypeArguments[0]; + if (typeArgument is ITypeParameterSymbol) + { + IfeCompilerMessages.SGFE011(SystemDescription, errorLocation); + return (QueryType.Invalid, false); + } + + bool isQueryTypeEnableable = false; + if (typeArgument is INamedTypeSymbol namedTypeSymbol && namedTypeSymbol.Arity != 0) + { + // If T is itself generic + if (HasTypeParameter(typeArgument)) + { + IfeCompilerMessages.SGFE010(SystemDescription, errorLocation); + return (QueryType.Invalid, false); + } + + // T is not generic + var componentType = namedTypeSymbol.TypeArguments[0]; + isQueryTypeEnableable = componentType.IsEnableableComponent(); + } + + return typeSymbol.Name switch + { + "DynamicBuffer" => (QueryType.DynamicBuffer, false), + "RefRW" => (QueryType.RefRW, isQueryTypeEnableable), + "RefRO" => (QueryType.RefRO, isQueryTypeEnableable), + "EnabledRefRW" => (QueryType.EnabledRefRW, true), + "EnabledRefRO" => (QueryType.EnabledRefRO, true), + "UnityEngineComponent" => (QueryType.UnityEngineComponent, false), + _ => throw new ArgumentOutOfRangeException() + }; + } + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeDescription.cs new file mode 100644 index 0000000..6f0a3ea --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeDescription.cs @@ -0,0 +1,562 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Operations; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; + +using static Unity.Entities.SourceGen.Common.SourceGenHelpers; + +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +public partial class IfeDescription +{ + internal IfeType IfeType { get; } + internal bool IsBurstEnabled { get; } + internal Location Location { get; } + internal SystemDescription SystemDescription { get; } + + internal IReadOnlyList NoneQueryTypes => _noneQueryTypes; + internal IReadOnlyList AnyQueryTypes => _anyQueryTypes; + internal IReadOnlyList AllQueryTypes => _allQueryTypes; + internal IReadOnlyList DisabledQueryTypes => _disabledQueryTypes; + internal IReadOnlyList AbsentQueryTypes => _absentQueryTypes; + internal IReadOnlyList PresentQueryTypes => _presentQueryTypes; + internal IReadOnlyList ChangeFilterQueryTypes => _changeFilterQueryTypes; + internal IReadOnlyList IterableNonEnableableTypes => _iterableNonEnableableTypes; + + internal IReadOnlyList SharedComponentFilterInfos => _sharedComponentFilterInfos; + + internal HashSet GetDistinctAllQueryTypes() + { + var distinctAll = new HashSet(); + + // Add all .WithAll types + foreach (var q in _allQueryTypes) + distinctAll.Add(q); + + // Add all shared component filter types + foreach (var q in SharedComponentFilterInfos) + distinctAll.Add(new Common.Query + { + IsReadOnly = true, + Type = Common.QueryType.All, + TypeSymbol = q.TypeSymbol + }); + + // Add all SystemAPI.Query types, where T is not enableable + foreach (var q in IterableNonEnableableTypes) + distinctAll.Add(q); + + // Add all SystemAPI.Query types, where T is enableable *and* not used in `.WithAny`, `.WithNone` and `.WithDisabled` + foreach (var q in IterableEnableableQueryDatasToBeTreatedAsAllComponents) + { + distinctAll.Add(new Common.Query + { + IsReadOnly = q.IsReadOnly, + Type = Common.QueryType.All, + TypeSymbol = q.QueriedTypeSymbol + }); + } + + return distinctAll; + } + + internal EntityQueryOptions GetEntityQueryOptionsArgument() + { + if (!_entityQueryOptionsArguments.Any()) + return EntityQueryOptions.Default; + + var options = EntityQueryOptions.Default; + + var argumentExpression = _entityQueryOptionsArguments.First().Expression; + + while (argumentExpression is BinaryExpressionSyntax binaryExpressionSyntax) + { + if (TryParseQualifiedEnumValue(binaryExpressionSyntax.Right.ToString(), out EntityQueryOptions optionArg)) + options |= optionArg; + + argumentExpression = binaryExpressionSyntax.Left; + } + + if (TryParseQualifiedEnumValue(argumentExpression.ToString(), out EntityQueryOptions option)) + options |= option; + + return options; + } + + readonly List _noneQueryTypes = new(); + readonly List _anyQueryTypes = new(); + readonly List _allQueryTypes = new(); + readonly List _disabledQueryTypes = new(); + readonly List _absentQueryTypes = new(); + readonly List _presentQueryTypes = new(); + readonly List _changeFilterQueryTypes = new(); + readonly List _iterableNonEnableableTypes = new(); + readonly List _sharedComponentFilterInfos = new(); + readonly List _entityQueryOptionsArguments = new(); + readonly bool _entityAccessRequired; + readonly string _systemStateName; + readonly string _generatedIfeTypeFullyQualifiedName; + + IList AllIterableQueryDatas { get; set; } + HashSet InitialIterableEnableableTypeSymbols { get; set; } + IList InitialIterableEnableableQueryDatas { get; set; } + List IterableEnableableQueryDatasToBeTreatedAsAllComponents { get; set; } + AttributeData BurstCompileAttribute { get; } + + public bool Success { get; internal set; } = true; + + public readonly QueryCandidate QueryCandidate; + public readonly List AdditionalCandidates = new(); + public readonly Dictionary CandidateNodesToReplacementCode; + + public string CreateQueryInvocationNodeReplacementCode(string entityQueryFieldName, string ifeTypeHandleFieldName) => + $"{_generatedIfeTypeFullyQualifiedName}.Query({entityQueryFieldName}, __TypeHandle.{ifeTypeHandleFieldName}, ref {_systemStateName}" + + $"{string.Join("", SharedComponentFilterInfos.Select(p => $", {p.Argument}"))})"; + + public IfeDescription(SystemDescription systemDescription, QueryCandidate queryCandidate, int numForEachsPreviouslySeenInSystem) + { + if (systemDescription.SemanticModel.GetOperation(queryCandidate.FullInvocationChainSyntaxNode) is IInvocationOperation invocationOperation + && IsSystemApiQueryInvocation(invocationOperation)) + { + QueryCandidate = queryCandidate; + var queryCandidateContainingStatement = queryCandidate.FullInvocationChainSyntaxNode.AncestorOfKindOrDefault(); + + SystemDescription = systemDescription; + Location = queryCandidate.FullInvocationChainSyntaxNode.GetLocation(); + + if (!TryGetQueryDatas()) + { + Success = false; + return; + } + + var containingMethod = queryCandidate.FullInvocationChainSyntaxNode.AncestorOfKindOrDefault(); + if (containingMethod != null) + { + var methodSymbol = SystemDescription.SemanticModel.GetDeclaredSymbol(containingMethod); + + var queryExtensionInvocations = queryCandidate.FullInvocationChainSyntaxNode.DescendantNodesAndSelf().OfType(); + foreach (var node in queryExtensionInvocations) + { + switch (node.Expression) + { + case MemberAccessExpressionSyntax { Name: GenericNameSyntax genericNameSyntax }: + { + var extensionMethodName = genericNameSyntax.Identifier.ValueText; + switch (extensionMethodName) + { + case "WithAny": + case "WithNone": + case "WithDisabled": + case "WithPresent": + foreach (var typeArg in genericNameSyntax.TypeArgumentList.Arguments) + { + var typeArgSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type; + + bool isReadOnly = true; + + // We support writing: + // `SystemAPI.Query>().WithDisabled()`, + // `SystemAPI.Query>().WithAny()`, + // `SystemAPI.Query>().WithPresent()`, + // and `SystemAPI.Query>().WithNone()`. + // If the type passed to e.g. `.WithDisabled()` is also something which the user wants to iterate through + if (InitialIterableEnableableTypeSymbols.Contains(typeArgSymbol)) + { + foreach (var iterableQueryData in InitialIterableEnableableQueryDatas) + { + if (SymbolEqualityComparer.Default.Equals(iterableQueryData.QueriedTypeSymbol, typeArgSymbol)) + { + // Figure out whether the user wants to iterate through it with readonly access. + // E.g. The user might write: `SystemAPI.Query, RefRO>().WithDisabled()` + // Even though`RefRO` requires read-only access, `EnabledRefRW` requires read-write access, + // which means that the query we create needs to request read-write access to T. + isReadOnly &= iterableQueryData.IsReadOnly; + + // All types remaining in `IterableEnableableQueryDatasToBeTreatedAsAllComponents` will be treated as `All` components when creating an EntityQuery. + // Since the current type must be treated as `Any`, `None`, or `Disabled`, we must remove it from `IterableEnableableQueryDatasToBeTreatedAsAllComponents`. + IterableEnableableQueryDatasToBeTreatedAsAllComponents.RemoveAll( + q => SymbolEqualityComparer.Default.Equals(q.QueriedTypeSymbol, typeArgSymbol)); + } + } + } + + if (extensionMethodName == "WithAny") + _anyQueryTypes.Add(new Common.Query + { + TypeSymbol = typeArgSymbol, + Type = Common.QueryType.Any, + IsReadOnly = isReadOnly + }); + else if (extensionMethodName == "WithNone") + _noneQueryTypes.Add(new Common.Query + { + TypeSymbol = typeArgSymbol, + Type = Common.QueryType.None, + IsReadOnly = isReadOnly + }); + else if (extensionMethodName == "WithPresent") + _presentQueryTypes.Add(new Common.Query + { + TypeSymbol = typeArgSymbol, + Type = Common.QueryType.Present, + IsReadOnly = isReadOnly + }); + else + _disabledQueryTypes.Add(new Common.Query + { + TypeSymbol = typeArgSymbol, + Type = Common.QueryType.Disabled, + IsReadOnly = isReadOnly + }); + } + break; + case "WithAbsent": + foreach (var typeArg in genericNameSyntax.TypeArgumentList.Arguments) + { + var typeArgSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type; + + // E.g. if users write `foreach (var x in SystemAPI.Query>().WithAbsent())` + if (InitialIterableEnableableTypeSymbols.Contains(typeArgSymbol)) + IfeCompilerMessages.SGFE012(SystemDescription, typeArgSymbol.ToFullName(), genericNameSyntax.GetLocation()); + else + _absentQueryTypes.Add(new Common.Query + { + TypeSymbol = typeArgSymbol, + Type = Common.QueryType.Absent, + IsReadOnly = true + }); + } + break; + case "WithAll": + foreach (var typeArg in genericNameSyntax.TypeArgumentList.Arguments) + { + var typeArgSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type; + + // We support writing `SystemAPI.Query>().WithAll()`. + // If the type passed to e.g. `.WithAll()` is also something which the user wants to iterate through, then we don't need to do anything -- + // we have already previously registered T. + if (InitialIterableEnableableTypeSymbols.Contains(typeArgSymbol)) + continue; + + _allQueryTypes.Add(new Common.Query + { + TypeSymbol = typeArgSymbol, + Type = Common.QueryType.All, + IsReadOnly = true + }); + } + break; + case "WithChangeFilter": + _changeFilterQueryTypes.AddRange( + genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => + new Common.Query + { + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, + Type = Common.QueryType.ChangeFilter, + IsReadOnly = true + })); + break; + case "WithSharedComponentFilterManaged": + case "WithSharedComponentFilter": + var typeArgs = genericNameSyntax.TypeArgumentList.Arguments; + var args = node.ArgumentList.Arguments; + for (var index = 0; index < typeArgs.Count; index++) + { + var typeArg = typeArgs[index]; + var typeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type; + var arg = args[index]; + _sharedComponentFilterInfos.Add( + new SharedComponentFilterInfo + { + Argument = arg, + TypeSymbol = typeSymbol, + IsManaged = genericNameSyntax.Identifier.ValueText == "WithSharedComponentFilterManaged" + }); + } + break; + } + break; + } + case MemberAccessExpressionSyntax { Name: IdentifierNameSyntax identifierNameSyntax }: + { + switch (identifierNameSyntax.Identifier.ValueText) + { + case "WithSharedComponentFilterManaged": + case "WithSharedComponentFilter": + var args = node.ArgumentList.Arguments; + foreach (var arg in args) + { + var typeSymbol = systemDescription.SemanticModel.GetTypeInfo(arg.Expression).Type; + _sharedComponentFilterInfos.Add( + new SharedComponentFilterInfo + { + Argument = arg, + TypeSymbol = typeSymbol, + IsManaged = identifierNameSyntax.Identifier.ValueText == "WithSharedComponentFilterManaged" + }); + } + break; + case "WithOptions": + _entityQueryOptionsArguments.Add(node.ArgumentList.Arguments.Single()); + break; + case "WithEntityAccess": + _entityAccessRequired = true; + break; + } + break; + } + } + } + + bool isQueryInForEach = queryCandidateContainingStatement is CommonForEachStatementSyntax; + if (!isQueryInForEach) + { + IfeCompilerMessages.SGFE001(SystemDescription, Location); + Success = false; + return; + } + + if (_changeFilterQueryTypes.Count > 2) + IfeCompilerMessages.SGFE003(SystemDescription, _changeFilterQueryTypes.Count, Location); + if (SharedComponentFilterInfos.Count > 2) + IfeCompilerMessages.SGFE007(SystemDescription, SharedComponentFilterInfos.Count, Location); + if (_entityQueryOptionsArguments.Count > 1) + IfeCompilerMessages.SGFE008(SystemDescription, _entityQueryOptionsArguments.Count, Location); + + if (SystemDescription.TryGetSystemStateParameterName(QueryCandidate, out var systemStateExpression)) + _systemStateName = systemStateExpression.ToFullString(); + else + { + Success = false; + return; + } + + var burstCompileAttribute = methodSymbol.GetAttributes().SingleOrDefault(a => a.AttributeClass.ToFullName() == "Unity.Burst.BurstCompileAttribute"); + + IsBurstEnabled = burstCompileAttribute != null; + BurstCompileAttribute = burstCompileAttribute; + string ifeTypeName = $"IFE_{systemDescription.QueriesAndHandles.UniqueId}_{numForEachsPreviouslySeenInSystem}"; + + var ifeType = new IfeType + { + PerformsCollectionChecks = SystemDescription.PreprocessorInfo.IsUnityCollectionChecksEnabled, + MustReturnEntityDuringIteration = _entityAccessRequired, + TypeName = ifeTypeName, + BurstCompileAttribute = BurstCompileAttribute, + FullyQualifiedTypeName = GetIfeTypeFullyQualifiedTypeName(queryCandidate, ifeTypeName), + ReturnedTupleElementsDuringEnumeration = + AllIterableQueryDatas + .Select((queryData, index) => + { + string typeArgumentFullName; + switch (queryData.QueryType) + { + case QueryType.RefRO: + typeArgumentFullName = queryData.TypeParameterSymbol.ToFullName(); + return new ReturnedTupleElementDuringEnumeration( + $"Unity.Entities.Internal.InternalCompilerInterface.UncheckedRefRO<{typeArgumentFullName}>", + typeArgumentFullName: typeArgumentFullName, + elementName: $"item{index + 1}", + type: queryData.QueryType); + case QueryType.RefRW: + typeArgumentFullName = queryData.TypeParameterSymbol.ToFullName(); + return new ReturnedTupleElementDuringEnumeration( + $"Unity.Entities.Internal.InternalCompilerInterface.UncheckedRefRW<{typeArgumentFullName}>", + typeArgumentFullName: typeArgumentFullName, + elementName: $"item{index + 1}", + type: queryData.QueryType); + default: + typeArgumentFullName = queryData.TypeParameterSymbol is { } symbol ? symbol.ToFullName() : string.Empty; + return new ReturnedTupleElementDuringEnumeration( + queryData.TypeSymbolFullName, + typeArgumentFullName: typeArgumentFullName, + elementName: $"item{index + 1}", + type: queryData.QueryType); + } + }) + .ToArray() + }; + + IfeType = ifeType; + _generatedIfeTypeFullyQualifiedName = ifeType.FullyQualifiedTypeName; + CandidateNodesToReplacementCode = new Dictionary(); + + switch (queryCandidateContainingStatement) + { + case ForEachVariableStatementSyntax { Variable: TupleExpressionSyntax tupleExpressionSyntax }: + { + // Original code: foreach ((RefRW a, RefRO b) in SystemAPI.Query, RefRO>()) + // Patched code: foreach ((UncheckedRefRW a, UncheckedRefRO b) in generatedIfeEnumerator) + for (var index = 0; index < tupleExpressionSyntax.Arguments.Count; index++) + { + if (tupleExpressionSyntax.Arguments[index].Expression is DeclarationExpressionSyntax declarationExpressionSyntax) + { + var typeSyntax = declarationExpressionSyntax.Type; + if (typeSyntax is GenericNameSyntax genericNameSyntax && genericNameSyntax.Identifier.ValueText != "var") + { + var queryData = AllIterableQueryDatas.ElementAt(index); + var typeParamSymbolFullName = queryData.TypeParameterSymbol.ToFullName(); + + switch (queryData.QueryType) + { + case QueryType.RefRO: + CandidateNodesToReplacementCode.Add(genericNameSyntax, $"Unity.Entities.Internal.InternalCompilerInterface.UncheckedRefRO<{typeParamSymbolFullName}> "); + AdditionalCandidates.Add(new CandidateSyntax(CandidateType.Ife, CandidateFlags.None, genericNameSyntax)); + break; + case QueryType.RefRW: + CandidateNodesToReplacementCode.Add(genericNameSyntax, $"Unity.Entities.Internal.InternalCompilerInterface.UncheckedRefRW<{typeParamSymbolFullName}> "); + AdditionalCandidates.Add(new CandidateSyntax(CandidateType.Ife, CandidateFlags.None, genericNameSyntax)); + break; + } + } + } + } + break; + } + case ForEachStatementSyntax forEachStatementSyntax: + { + switch (forEachStatementSyntax.Type) + { + // Original code: foreach ((RefRW, RefRO) result in SystemAPI.Query, RefRO>()) + // Patched code: foreach ((UncheckedRefRW, UncheckedRefRO) result in generatedIfeEnumerator) + case TupleTypeSyntax tupleTypeSyntax: + { + for (int i = 0; i < AllIterableQueryDatas.Count; i++) + { + var queryData = AllIterableQueryDatas.ElementAt(i); + var typeSyntax = tupleTypeSyntax.Elements[i].Type; + + if (typeSyntax is GenericNameSyntax genericNameSyntax) + { + var typeParamSymbolFullName = queryData.TypeParameterSymbol.ToFullName(); + switch (queryData.QueryType) + { + case QueryType.RefRW: + CandidateNodesToReplacementCode.Add(genericNameSyntax, $"Unity.Entities.Internal.InternalCompilerInterface.UncheckedRefRW<{typeParamSymbolFullName}> "); + AdditionalCandidates.Add(new CandidateSyntax(CandidateType.Ife, CandidateFlags.None, genericNameSyntax)); + break; + case QueryType.RefRO: + CandidateNodesToReplacementCode.Add(genericNameSyntax, $"Unity.Entities.Internal.InternalCompilerInterface.UncheckedRefRO<{typeParamSymbolFullName}> "); + AdditionalCandidates.Add(new CandidateSyntax(CandidateType.Ife, CandidateFlags.None, genericNameSyntax)); + break; + } + } + } + break; + } + case GenericNameSyntax genericNameSyntax: + { + var queryData = AllIterableQueryDatas.Single(); + var queryType = queryData.QueryType; + + // Original code: foreach (RefRW result in SystemAPI.Query>()) + // Patched code: foreach (UncheckedRefRW result in generatedIfeEnumerator) + if (genericNameSyntax.Identifier.ValueText != "var") + { + var typeParamSymbolFullName = queryData.TypeParameterSymbol.ToFullName(); + switch (queryType) + { + case QueryType.RefRO: + CandidateNodesToReplacementCode.Add(genericNameSyntax, $"Unity.Entities.Internal.InternalCompilerInterface.UncheckedRefRO<{typeParamSymbolFullName}> "); + AdditionalCandidates.Add(new CandidateSyntax(CandidateType.Ife, CandidateFlags.None, genericNameSyntax)); + break; + case QueryType.RefRW: + CandidateNodesToReplacementCode.Add(genericNameSyntax, $"Unity.Entities.Internal.InternalCompilerInterface.UncheckedRefRW<{typeParamSymbolFullName}> "); + AdditionalCandidates.Add(new CandidateSyntax(CandidateType.Ife, CandidateFlags.None, genericNameSyntax)); + break; + } + } + break; + } + } + break; + } + } + } + else + { + var propertyDeclarationSyntax = queryCandidate.FullInvocationChainSyntaxNode.AncestorOfKind(); + var propertySymbol = ModelExtensions.GetDeclaredSymbol(systemDescription.SemanticModel, propertyDeclarationSyntax); + + IfeCompilerMessages.SGFE002( + systemDescription, + propertySymbol.OriginalDefinition.ToString(), + queryCandidate.FullInvocationChainSyntaxNode.GetLocation()); + Success = false; + } + } + else + Success = false; + + static bool IsSystemApiQueryInvocation(IInvocationOperation operation) + { + var constructedFrom = operation.TargetMethod.ConstructedFrom.ToString(); + if (constructedFrom.StartsWith("Unity.Entities.QueryEnumerable<")) + return true; + if (constructedFrom.StartsWith("Unity.Entities.QueryEnumerableWithEntity<")) + return true; + return constructedFrom.StartsWith("Unity.Entities.SystemAPI.Query<"); + } + + static string GetIdentifier(MemberDeclarationSyntax memberDeclarationSyntax) + { + switch (memberDeclarationSyntax) + { + case ClassDeclarationSyntax classDeclarationSyntax: + return classDeclarationSyntax.Identifier.ValueText; + case StructDeclarationSyntax structDeclarationSyntax: + return structDeclarationSyntax.Identifier.ValueText; + case NamespaceDeclarationSyntax namespaceDeclarationSyntax: + var identifierName = namespaceDeclarationSyntax.ChildNodes().OfType().FirstOrDefault(); + return + identifierName != null + ? identifierName.Identifier.ValueText + : namespaceDeclarationSyntax.ChildNodes().OfType().First().ToString(); + default: + throw new ArgumentOutOfRangeException(); + } + } + + static string GetIfeTypeFullyQualifiedTypeName(QueryCandidate queryCandidate, string unqualifiedTypeName) + { + var stack = new Stack(); + + foreach (var parentClassOrNamespace in GetContainingTypesAndNamespacesFromMostToLeastNested(queryCandidate.FullInvocationChainSyntaxNode)) + { + var identifier = GetIdentifier(parentClassOrNamespace); + stack.Push(identifier); + } + + if (stack.Count == 0) + return unqualifiedTypeName; + + using var fullyQualifiedNameWriter = new StringWriter(); + + while (stack.Count > 0) + { + var name = stack.Pop(); + fullyQualifiedNameWriter.Write(name); + fullyQualifiedNameWriter.Write("."); + } + fullyQualifiedNameWriter.Write(unqualifiedTypeName); + return fullyQualifiedNameWriter.ToString(); + + IEnumerable GetContainingTypesAndNamespacesFromMostToLeastNested(SyntaxNode syntaxNode) + { + var current = syntaxNode; + while (current.Parent is NamespaceDeclarationSyntax or ClassDeclarationSyntax or StructDeclarationSyntax) + { + yield return current.Parent as MemberDeclarationSyntax; + current = current.Parent; + } + } + } + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeModule.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeModule.cs new file mode 100644 index 0000000..53e745a --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeModule.cs @@ -0,0 +1,197 @@ +using System.Collections.Generic; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; +using static Unity.Entities.SourceGen.SystemGenerator.Common.QueryVerification; + +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +public class IfeModule : ISystemModule +{ + private readonly List _queryCandidates = new(); + private Dictionary _candidatesGroupedByContainingSystemTypes; + + private Dictionary CandidatesGroupedByContainingSystemTypes + { + get + { + if (_candidatesGroupedByContainingSystemTypes == null) + { + _candidatesGroupedByContainingSystemTypes = + _queryCandidates + .GroupBy(c => c.ContainingTypeNode) + .ToDictionary(group => group.Key, group => group.ToArray()); + } + return _candidatesGroupedByContainingSystemTypes; + } + } + + public IEnumerable<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)> Candidates + => _queryCandidates.Select(candidate => (SyntaxNode: candidate.FullInvocationChainSyntaxNode, ContainingType: candidate.ContainingTypeNode)); + + public bool RequiresReferenceToBurst { get; private set; } + + public void OnReceiveSyntaxNode(SyntaxNode node, Dictionary candidateOwnership) + { + if (node is InvocationExpressionSyntax invocationExpressionSyntax) + { + switch (invocationExpressionSyntax.Expression) + { + case MemberAccessExpressionSyntax memberAccessExpressionSyntax: + { + switch (memberAccessExpressionSyntax.Name) + { + case GenericNameSyntax { Identifier: { ValueText: "Query" } } genericNameSyntax: + { + var fullInvocationChainSyntaxNode = invocationExpressionSyntax.Ancestors().OfType().LastOrDefault() ?? invocationExpressionSyntax; + + var candidate = QueryCandidate.From(fullInvocationChainSyntaxNode, genericNameSyntax.TypeArgumentList.Arguments); + _queryCandidates.Add(candidate); + + var candidateSyntax = new CandidateSyntax(CandidateType.Ife, CandidateFlags.None, fullInvocationChainSyntaxNode); + candidateOwnership[fullInvocationChainSyntaxNode] = candidateSyntax; + break; + } + } + break; + } + case GenericNameSyntax { Identifier: { ValueText: "Query" } } genericNameSyntax: + { + var fullInvocationChainSyntaxNode = invocationExpressionSyntax.Ancestors().OfType().LastOrDefault() ?? invocationExpressionSyntax; + + var candidate = QueryCandidate.From(fullInvocationChainSyntaxNode, genericNameSyntax.TypeArgumentList.Arguments); + _queryCandidates.Add(candidate); + + var candidateSyntax = new CandidateSyntax(CandidateType.Ife, CandidateFlags.None, fullInvocationChainSyntaxNode); + candidateOwnership[fullInvocationChainSyntaxNode] = candidateSyntax; + break; + } + } + } + } + + public bool RegisterChangesInSystem(SystemDescription systemDescription) + { + var idiomaticCSharpForEachDescriptions = new List(); + foreach (var queryCandidate in CandidatesGroupedByContainingSystemTypes[systemDescription.SystemTypeSyntax]) + { + var description = new IfeDescription(systemDescription, queryCandidate, idiomaticCSharpForEachDescriptions.Count); + + description.Success &= + VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.AllQueryTypes, invokedMethodName: "WithAll"); + description.Success &= + VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.NoneQueryTypes, invokedMethodName: "WithNone"); + description.Success &= + VerifyQueryTypeCorrectness(description.SystemDescription, description.Location, description.AnyQueryTypes, invokedMethodName: "WithAny"); + + description.Success &= + VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.AbsentQueryTypes, description.PresentQueryTypes, "WithAbsent", "WithPresent"); + description.Success &= + VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.NoneQueryTypes, description.PresentQueryTypes, "WithNone", "WithPresent"); + description.Success &= + VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.AnyQueryTypes, description.PresentQueryTypes, "WithAny", "WithPresent"); + description.Success &= + VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.AbsentQueryTypes, description.DisabledQueryTypes, "WithAbsent", "WithDisabled"); + description.Success &= + VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.AbsentQueryTypes, description.AllQueryTypes, "WithAbsent", "WithAll"); + description.Success &= + VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.AbsentQueryTypes, description.AnyQueryTypes, "WithAbsent", "WithAny"); + description.Success &= + VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.NoneQueryTypes, description.AllQueryTypes, "WithNone", "WithAll"); + description.Success &= + VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.NoneQueryTypes, description.AnyQueryTypes, "WithNone", "WithAny"); + description.Success &= + VerifyNoMutuallyExclusiveQueries(description.SystemDescription, description.Location, description.AnyQueryTypes, description.AllQueryTypes, "WithAny", "WithAll"); + + if (!description.Success) // if !description.Success, TypeSymbolsForEntityQueryCreation might not be right, thus causing an exception + continue; + + description.Success &= VerifyNoMutuallyExclusiveQueries( + description.SystemDescription, + description.Location, + description.AbsentQueryTypes, + description.IterableNonEnableableTypes, + "WithAbsent", + "Main query type in SystemAPI.Query", + compareTypeSymbolsOnly: true); + + description.Success &= VerifyNoMutuallyExclusiveQueries( + description.SystemDescription, + description.Location, + description.DisabledQueryTypes, + description.IterableNonEnableableTypes, + "WithDisabled", + "Main query type in SystemAPI.Query", + compareTypeSymbolsOnly: true); + + description.Success &= VerifyNoMutuallyExclusiveQueries( + description.SystemDescription, + description.Location, + description.NoneQueryTypes, + description.IterableNonEnableableTypes, + "WithNone", + "Main query type in SystemAPI.Query", + compareTypeSymbolsOnly: true); + + description.Success &= VerifyNoMutuallyExclusiveQueries( + description.SystemDescription, + description.Location, + description.AnyQueryTypes, + description.IterableNonEnableableTypes, + "WithAny", + "Main query type in SystemAPI.Query", + compareTypeSymbolsOnly: true); + + if (description.Success) + { + if (description.IsBurstEnabled) + RequiresReferenceToBurst = true; + + idiomaticCSharpForEachDescriptions.Add(description); + } + } + + var candidateNodesToReplacementCode = new Dictionary(); + foreach (var description in idiomaticCSharpForEachDescriptions) + { + var ifeStructWriter = + new IfeStructWriter + { + IfeType = description.IfeType, + SharedComponentFilterInfos = description.SharedComponentFilterInfos + }; + + systemDescription.NewMiscellaneousMembers.Add(ifeStructWriter); + + var ifeTypeHandleFieldName = + systemDescription.QueriesAndHandles.GetOrCreateSourceGeneratedTypeHandleField(description.IfeType.FullyQualifiedTypeName); + + var entityQueryFieldName = + systemDescription.QueriesAndHandles.GetOrCreateQueryField( + new SingleArchetypeQueryFieldDescription( + new Archetype( + description.GetDistinctAllQueryTypes(), + description.AnyQueryTypes, + description.NoneQueryTypes, + description.DisabledQueryTypes, + description.AbsentQueryTypes, + description.PresentQueryTypes, + description.GetEntityQueryOptionsArgument()), + description.ChangeFilterQueryTypes)); + + description.CandidateNodesToReplacementCode.Add( + key: description.QueryCandidate.FullInvocationChainSyntaxNode, + value: description.CreateQueryInvocationNodeReplacementCode(entityQueryFieldName, ifeTypeHandleFieldName)); + + foreach (var candidateSyntax in description.AdditionalCandidates) + systemDescription.CandidateNodes.Add(candidateSyntax.Node, candidateSyntax); + + foreach (var kvp in description.CandidateNodesToReplacementCode) + candidateNodesToReplacementCode.Add(kvp.Key, kvp.Value); + } + systemDescription.SyntaxWalkers.Add(Module.Ife, new IfeSyntaxWalker(candidateNodesToReplacementCode)); + return true; + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeStructWriter.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeStructWriter.cs new file mode 100644 index 0000000..668df9c --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeStructWriter.cs @@ -0,0 +1,324 @@ +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Linq; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +struct IfeStructWriter : IMemberWriter +{ + public IfeType IfeType { get; set; } + public IReadOnlyList SharedComponentFilterInfos { get; set; } + + void GenerateCompleteDependenciesMethod(IndentedTextWriter writer) + { + writer.WriteLine("public static void CompleteDependencyBeforeRW(ref SystemState state)"); + writer.WriteLine("{"); + writer.Indent++; + + foreach (var element in IfeType.ReturnedTupleElementsDuringEnumeration) + { + writer.WriteLine(element.Type switch + { + QueryType.ManagedComponent => + $"state.EntityManager.CompleteDependencyBeforeRW<{element.TypeSymbolFullName}>();", + QueryType.UnityEngineComponent => + $"state.EntityManager.CompleteDependencyBeforeRW<{element.TypeArgumentFullName}>();", + QueryType.RefRW => + $"state.EntityManager.CompleteDependencyBeforeRW<{element.TypeArgumentFullName}>();", + QueryType.RefRO => + $"state.EntityManager.CompleteDependencyBeforeRO<{element.TypeArgumentFullName}>();", + QueryType.UnmanagedSharedComponent => + $"state.EntityManager.CompleteDependencyBeforeRO<{element.TypeSymbolFullName}>();", + QueryType.ManagedSharedComponent => + $"state.EntityManager.CompleteDependencyBeforeRW<{element.TypeSymbolFullName}>();", + QueryType.Aspect => $"default({element.TypeSymbolFullName}).CompleteDependencyBeforeRW(ref state);", + QueryType.DynamicBuffer => + $"state.EntityManager.CompleteDependencyBeforeRW<{element.TypeArgumentFullName}>();", + QueryType.ValueTypeComponent => + $"state.EntityManager.CompleteDependencyBeforeRO<{element.TypeSymbolFullName}>();", + QueryType.EnabledRefRW => + $"state.EntityManager.CompleteDependencyBeforeRW<{element.TypeArgumentFullName}>();", + QueryType.EnabledRefRO => + $"state.EntityManager.CompleteDependencyBeforeRO<{element.TypeArgumentFullName}>();", + QueryType.TagComponent => "", + _ => throw new ArgumentOutOfRangeException() + }); + } + + writer.Indent--; + writer.WriteLine("}"); + } + + void GenerateEnumerator( + IndentedTextWriter writer, + string ifeTypeName, + string queryResultTypeName) + { + if (IfeType.UseBurst) + { + writer.WriteLine("[global::Unity.Burst.NoAlias]"); + writer.WriteLine($"[{IfeType.BurstCompileAttribute}]"); + } + + writer.WriteLine($"public struct Enumerator : global::System.Collections.Generic.IEnumerator<{queryResultTypeName}>"); + writer.WriteLine("{"); + writer.Indent++; + + // Create EntityQuery field if there are SharedComponentFilterTypes, because we need to invoke query.ResetFilter() at the end of the enumeration. + if (SharedComponentFilterInfos.Count > 0) + writer.WriteLine("global::Unity.Entities.EntityQuery _entityQuery;"); + + writer.WriteLine("global::Unity.Entities.Internal.InternalEntityQueryEnumerator _entityQueryEnumerator;"); + writer.WriteLine("TypeHandle _typeHandle;"); + writer.WriteLine("ResolvedChunk _resolvedChunk;"); + writer.WriteLine(); + writer.WriteLine("int _currentEntityIndex;"); + writer.WriteLine("int _endEntityIndex;"); + writer.WriteLine(); + + // Enumerator constructor should have SharedComponent parameters if .WithSharedComponentFilter() is used, + // because we need to invoke query.SetSharedComponentFilter(sharedComp). + writer.Write("public Enumerator(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle, ref Unity.Entities.SystemState state"); + + for (var index = 0; index < SharedComponentFilterInfos.Count; index++) + { + var info = SharedComponentFilterInfos[index]; + + writer.Write(", "); + writer.Write(info.TypeSymbol.ToFullName()); + writer.Write(" sharedComponent" + index); + } + + writer.Write(")"); + writer.WriteLine(); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("if (!entityQuery.IsEmptyIgnoreFilter)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine($"{ifeTypeName}.CompleteDependencyBeforeRW(ref state);"); + writer.WriteLine("typeHandle.Update(ref state);"); + writer.WriteLine(); + + for (var index = 0; index < SharedComponentFilterInfos.Count; index++) + { + var sharedComponentFilterInfo = SharedComponentFilterInfos[0]; + if (sharedComponentFilterInfo.IsManaged) + writer.WriteLine("entityQuery.SetSharedComponentFilterManaged(sharedComponent" + index + ");"); + else + writer.WriteLine("entityQuery.SetSharedComponentFilter(sharedComponent" + index + ");"); + } + + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + + if (SharedComponentFilterInfos.Count > 0) + writer.WriteLine("_entityQuery = entityQuery;"); + + writer.WriteLine("_entityQueryEnumerator = new global::Unity.Entities.Internal.InternalEntityQueryEnumerator(entityQuery);"); + writer.WriteLine(); + writer.WriteLine("_currentEntityIndex = -1;"); + writer.WriteLine("_endEntityIndex = -1;"); + writer.WriteLine(); + writer.WriteLine("_typeHandle = typeHandle;"); + writer.WriteLine("_resolvedChunk = default;"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine("public void Dispose() => _entityQueryEnumerator.Dispose();"); + writer.WriteLine(); + writer.WriteLine( + "[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine("public bool MoveNext()"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("_currentEntityIndex++;"); + writer.WriteLine(); + writer.WriteLine(IfeType.UseBurst + ? "if (global::Unity.Burst.CompilerServices.Hint.Unlikely(_currentEntityIndex >= _endEntityIndex))" + : "if (_currentEntityIndex >= _endEntityIndex)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine(IfeType.UseBurst + ? "if (global::Unity.Burst.CompilerServices.Hint.Likely(_entityQueryEnumerator.MoveNextEntityRange(out bool movedToNewChunk, out global::Unity.Entities.ArchetypeChunk chunk, out int entityStartIndex, out int entityEndIndex)))" + : "if (_entityQueryEnumerator.MoveNextEntityRange(out bool movedToNewChunk, out global::Unity.Entities.ArchetypeChunk chunk, out int entityStartIndex, out int entityEndIndex))"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("if (movedToNewChunk)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("_resolvedChunk = _typeHandle.Resolve(chunk);"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine("_currentEntityIndex = entityStartIndex;"); + writer.WriteLine("_endEntityIndex = entityEndIndex;"); + writer.WriteLine("return true;"); + writer.Indent--; + writer.WriteLine("}"); + + // We have reached the end of the enumeration. Reset filter if necessary. + if (SharedComponentFilterInfos.Count > 0) + writer.WriteLine("_entityQuery.ResetFilter();"); + + writer.WriteLine("return false;"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("return true;"); + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine($"public {queryResultTypeName} Current => _resolvedChunk.Get(_currentEntityIndex);"); + writer.WriteLine(); + writer.WriteLine("public Enumerator GetEnumerator() => this;"); + writer.WriteLine("public void Reset() => throw new global::System.NotImplementedException();"); + writer.WriteLine("object global::System.Collections.IEnumerator.Current => throw new global::System.NotImplementedException();"); + writer.Indent--; + writer.WriteLine("}"); + } + + public void WriteTo(IndentedTextWriter writer) + { + var resolvedChunk = + NestedStruct.ResolvedChunk(IfeType.ReturnedTupleElementsDuringEnumeration, + IfeType.MustReturnEntityDuringIteration, IfeType.PerformsCollectionChecks).ToArray(); + var typeHandle = + NestedStruct.TypeHandle(IfeType.ReturnedTupleElementsDuringEnumeration, + IfeType.MustReturnEntityDuringIteration, IfeType.PerformsCollectionChecks).ToArray(); + + (NestedStruct.Field ResolvedChunkField, NestedStruct.ArgumentInReturnedType TypeHandleArgument)[] + pairedFields = + resolvedChunk.Zip(typeHandle, (e1, e2) => + (ResolvedChunkField: e1.Field, TypeHandleField: e2.ArgumentWhenInitializingResolvedChunk)) + .ToArray(); + + var resultType = + IfeType.ResultType(queryResultConstructorArgs: + resolvedChunk + .Where(f => !f.ArgumentInReturnedTupleDuringIndexAccess.IsEmpty) + .Select(f => f.ArgumentInReturnedTupleDuringIndexAccess.Value)); + + writer.WriteLine(TypeCreationHelpers.GeneratedLineTriviaToGeneratedSource); + if (IfeType.UseBurst) + { + writer.WriteLine("[global::Unity.Burst.NoAlias]"); + writer.WriteLine($"[{IfeType.BurstCompileAttribute}]"); + } + + writer.WriteLine($"readonly struct {IfeType.TypeName}"); + writer.WriteLine("{"); + writer.Indent++; + if (IfeType.UseBurst) + { + writer.WriteLine("[global::Unity.Burst.NoAlias]"); + writer.WriteLine($"[{IfeType.BurstCompileAttribute}]"); + } + + writer.WriteLine($"public struct ResolvedChunk"); + writer.WriteLine("{"); + writer.Indent++; + + foreach (var field in resolvedChunk) + writer.WriteLine(field.Field.Declaration); + + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine($"public {resultType.FullName} Get(int index) => {resultType.Creation};"); + writer.Indent--; + writer.WriteLine("}"); + if (IfeType.UseBurst) + { + writer.WriteLine("[global::Unity.Burst.NoAlias]"); + writer.WriteLine($"[{IfeType.BurstCompileAttribute}]"); + } + + writer.WriteLine("public struct TypeHandle"); + writer.WriteLine("{"); + writer.Indent++; + bool needsEntManagerField = typeHandle.Any(f => f.Field.DependsOnEntityManagerField); + if (needsEntManagerField) + writer.WriteLine("public global::Unity.Entities.EntityManager _entityManager;"); + + var distinctTypeHandleContent = new HashSet(); + foreach (var t in typeHandle) + { + if (distinctTypeHandleContent.Add(t.Field.Declaration)) + writer.WriteLine(t.Field.Declaration); + } + + writer.WriteLine("public TypeHandle(ref global::Unity.Entities.SystemState systemState, bool isReadOnly)"); + writer.WriteLine("{"); + writer.Indent++; + if (needsEntManagerField) + writer.WriteLine("_entityManager = systemState.EntityManager;"); + + distinctTypeHandleContent.Clear(); + foreach (var t in typeHandle) + { + if (distinctTypeHandleContent.Add(t.Field.AssignmentInNestedStructConstructor)) + writer.WriteLine(t.Field.AssignmentInNestedStructConstructor); + } + + writer.Indent--; + writer.WriteLine("}"); + writer.WriteLine("public void Update(ref global::Unity.Entities.SystemState systemState)"); + writer.WriteLine("{"); + writer.Indent++; + + distinctTypeHandleContent.Clear(); + foreach (var kvp in typeHandle) + { + if (!string.IsNullOrEmpty(kvp.Field.Name) && distinctTypeHandleContent.Add(kvp.Field.Name)) + writer.WriteLine($"{kvp.Field.Name}.Update(ref systemState);"); + } + + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.WriteLine("public ResolvedChunk Resolve(global::Unity.Entities.ArchetypeChunk archetypeChunk)"); + writer.WriteLine("{"); + writer.Indent++; + writer.WriteLine("var resolvedChunk = new ResolvedChunk();"); + foreach (var s in NestedStruct.InitializeResolvedChunkInstanceInTypeHandle(pairedFields)) + writer.WriteLine(s); + + writer.WriteLine("return resolvedChunk;"); + writer.Indent--; + writer.WriteLine("}"); + writer.Indent--; + writer.WriteLine("}"); + + writer.WriteLine("[global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]"); + writer.Write("public static Enumerator Query(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle, ref Unity.Entities.SystemState state"); + + for (var index = 0; index < SharedComponentFilterInfos.Count; index++) + { + var info = SharedComponentFilterInfos[index]; + + writer.Write(", "); + writer.Write(info.TypeSymbol.ToFullName()); + writer.Write(" sharedComponent" + index); + } + writer.Write(")"); + writer.Write(" => new Enumerator(entityQuery, typeHandle, ref state"); + + for (var index = 0; index < SharedComponentFilterInfos.Count; index++) + { + writer.Write(", "); + writer.Write(" sharedComponent" + index); + } + + writer.Write(");"); + writer.WriteLine(); + + GenerateEnumerator(writer, IfeType.TypeName, resultType.FullName); + GenerateCompleteDependenciesMethod(writer); + + writer.Indent--; + writer.WriteLine("}"); + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeSyntaxWalker.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeSyntaxWalker.cs new file mode 100644 index 0000000..f71b2dc --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/IfeSyntaxWalker.cs @@ -0,0 +1,68 @@ +using System.CodeDom.Compiler; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +/* + The `IFESyntaxWalker` traverses through syntax nodes that have been marked by the `IFEModule` as candidates for patching. + It is much more straightforward than the `SystemApiWalker`, since it does not need to handle nested candidates. For illustration purposes, + let's use `foreach ((MyAspect, RefRW) queryReturnType in Query>())` as an example. + + The `SystemSyntaxWalker` walks the method that contains the `foreach` example above. When it reaches the `GenericNameSyntax` node + `RefRW`, which has been marked by the `IFEModule` as a candidate for patching, the `SystemSyntaxWalker` cedes write control to the `IFESyntaxWalker` + by calling `IFESyntaxWalker.TryWriteSyntax()`. The `IFESyntaxWalker` appends `InternalCompilerInterface.UncheckedRefRW`, and then returns control to + the `SystemSyntaxWalker`. The `SystemSyntaxWalker` then continues walking the method, and when it reaches the `Query>()` node, which + also has been marked by the `IFEModule` as a candidate for patching, it once again cedes write control to the `IFESyntaxWalker`. The `IFESyntaxWalker` appends + + IfeGeneratedType.Query(cachedQuery, cachedIfeTypeHandle, ref systemState) + + and immediately returns control again. The end result is that the `SystemSyntaxWalker` writes the following code: + + foreach ((MyAspect, InternalCompilerInterface.UncheckedRefRW) queryReturnType in IfeGeneratedType.Query(cachedQuery, cachedIfeTypeHandle, ref systemState)) + */ +public class IfeSyntaxWalker : CSharpSyntaxWalker, IModuleSyntaxWalker +{ + private IndentedTextWriter _writer; + private readonly IDictionary _candidateNodesToReplacementCode; + private bool _hasWrittenSyntax; + + public IfeSyntaxWalker(IDictionary candidateNodesToReplacementCode) : base(SyntaxWalkerDepth.Trivia) => + _candidateNodesToReplacementCode = candidateNodesToReplacementCode; + + public bool TryWriteSyntax(IndentedTextWriter writer, CandidateSyntax candidateSyntax) + { + _writer = writer; + _hasWrittenSyntax = false; + + // Begin depth-first traversal of the candidate node + Visit(candidateSyntax.Node); + + return _hasWrittenSyntax; + } + + public override void VisitInvocationExpression(InvocationExpressionSyntax node) + { + if (_candidateNodesToReplacementCode.TryGetValue(node, out var replacementCode)) + { + _writer.Write(replacementCode); + _hasWrittenSyntax = true; + } + else + _hasWrittenSyntax = false; + } + + public override void VisitGenericName(GenericNameSyntax node) + { + if (_candidateNodesToReplacementCode.TryGetValue(node, out var replacementCode)) + { + _writer.Write(replacementCode); + _hasWrittenSyntax = true; + } + else + _hasWrittenSyntax = false; + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/NestedStruct.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/NestedStruct.cs index 6079170..4a0b89e 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/NestedStruct.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/NestedStruct.cs @@ -1,522 +1,467 @@ using System; using System.Collections.Generic; -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +static class NestedStruct { - static class NestedStruct + public readonly struct Field { - public readonly struct Field + public readonly string Declaration; + public readonly string AssignmentInNestedStructConstructor; + public readonly string Name; + public readonly bool DependsOnEntityManagerField; + + public Field( + string declaration, + string name, + string assignmentInNestedStructConstructor = null, + bool dependsOnEntityManagerField = false) { - public readonly string Declaration; - public readonly string AssignmentInNestedStructConstructor; - public readonly string Name; - public readonly bool DependsOnEntityManagerField; - - public Field( - string declaration, - string name, - string assignmentInNestedStructConstructor = null, - bool dependsOnEntityManagerField = false) - { - Declaration = declaration; - AssignmentInNestedStructConstructor = assignmentInNestedStructConstructor; - Name = name; - DependsOnEntityManagerField = dependsOnEntityManagerField; - } + Declaration = declaration; + AssignmentInNestedStructConstructor = assignmentInNestedStructConstructor; + Name = name; + DependsOnEntityManagerField = dependsOnEntityManagerField; } + } - public readonly struct ArgumentInReturnedType - { - public readonly string Setup; - public readonly string Value; + public readonly struct ArgumentInReturnedType + { + public readonly string Setup; + public readonly string Value; - public bool RequiresSetup => !string.IsNullOrEmpty(Setup); - public bool IsEmpty => string.IsNullOrEmpty(Value); + public bool RequiresSetup => !string.IsNullOrEmpty(Setup); + public bool IsEmpty => string.IsNullOrEmpty(Value); - public ArgumentInReturnedType(string value, string setup = null) - { - Setup = setup; - Value = value; - } + public ArgumentInReturnedType(string value, string setup = null) + { + Setup = setup; + Value = value; } + } - internal static IEnumerable<(Field Field, ArgumentInReturnedType ArgumentInReturnedTupleDuringIndexAccess)> - ResolvedChunk(IReadOnlyCollection elements, bool provideEntityAccess, bool performCollectionChecks) + internal static IEnumerable<(Field Field, ArgumentInReturnedType ArgumentInReturnedTupleDuringIndexAccess)> + ResolvedChunk(IReadOnlyCollection elements, bool provideEntityAccess, bool performCollectionChecks) + { + foreach (var arg in elements) { - foreach (var arg in elements) + string fieldName; + string fieldDeclaration; + string elementInReturnedTuple; + + switch (arg.Type) { - string fieldName; - string fieldDeclaration; - string elementInReturnedTuple; - - switch (arg.Type) - { - case QueryType.Aspect: - fieldName = $"{arg.Name}_ResolvedChunk"; - fieldDeclaration = $"public {arg.TypeSymbolFullName}.ResolvedChunk {fieldName};"; - elementInReturnedTuple = $"{fieldName}[index]"; - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) - ); - break; - case QueryType.TagComponent: - fieldName = ""; - fieldDeclaration = ""; - elementInReturnedTuple = $"default({arg.TypeSymbolFullName})"; - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) - ); - break; - case QueryType.ValueTypeComponent: - fieldName = $"{arg.Name}_IntPtr"; - fieldDeclaration = $"public global::System.IntPtr {fieldName};"; - elementInReturnedTuple = $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement<{arg.TypeSymbolFullName}>({fieldName}, index)"; - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) - ); - break; - case QueryType.RefRW: - case QueryType.RefRW_TagComponent: - string typeHandleName = $"{arg.Name}_TypeHandle"; - - if (performCollectionChecks) - { - fieldName = typeHandleName; - fieldDeclaration = $"public Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; - - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(default) - ); - } - - fieldName = $"{arg.Name}_IntPtr"; - fieldDeclaration = $"public global::System.IntPtr {fieldName};"; - elementInReturnedTuple = - performCollectionChecks - ? $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRW<{arg.TypeArgumentFullName}>({fieldName}, index, ref {typeHandleName})" - : $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRW<{arg.TypeArgumentFullName}>({fieldName}, index)" ; + case QueryType.Aspect: + fieldName = $"{arg.Name}_ResolvedChunk"; + fieldDeclaration = $"public {arg.TypeSymbolFullName}.ResolvedChunk {fieldName};"; + elementInReturnedTuple = $"{fieldName}[index]"; + yield return + ( + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) + ); + break; + case QueryType.TagComponent: + fieldName = ""; + fieldDeclaration = ""; + elementInReturnedTuple = $"default({arg.TypeSymbolFullName})"; + yield return + ( + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) + ); + break; + case QueryType.ValueTypeComponent: + fieldName = $"{arg.Name}_IntPtr"; + fieldDeclaration = $"public global::System.IntPtr {fieldName};"; + elementInReturnedTuple = $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement<{arg.TypeSymbolFullName}>({fieldName}, index)"; + yield return + ( + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) + ); + break; + case QueryType.RefRW: + string typeHandleName = $"{arg.Name}_TypeHandle"; - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) - ); - break; - case QueryType.EnabledRefRW: - fieldName = $"{arg.Name}_EnabledMask"; - fieldDeclaration = $"public Unity.Entities.EnabledMask {fieldName};"; - elementInReturnedTuple = $"{fieldName}.GetEnabledRefRW<{arg.TypeArgumentFullName}>(index)"; - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) - ); - break; - case QueryType.RefRO: - case QueryType.RefRO_TagComponent: - string typeHandleName_= $"{arg.Name}_TypeHandle"; - - if (performCollectionChecks) - { - fieldName = typeHandleName_; - fieldDeclaration = $"[Unity.Collections.ReadOnly] public Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; - - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(default) - ); - } - - fieldName = $"{arg.Name}_IntPtr"; - fieldDeclaration = $"public global::System.IntPtr {fieldName};"; - elementInReturnedTuple = - performCollectionChecks - ? $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRO<{arg.TypeArgumentFullName}>({fieldName}, index, ref {typeHandleName_})" - : $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRO<{arg.TypeArgumentFullName}>({fieldName}, index)"; + if (performCollectionChecks) + { + fieldName = typeHandleName; + fieldDeclaration = $"public Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; yield return ( new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) - ); - break; - case QueryType.EnabledRefRO: - fieldName = $"{arg.Name}_EnabledMask"; - fieldDeclaration = $"public Unity.Entities.EnabledMask {fieldName};"; - elementInReturnedTuple = $"{fieldName}.GetEnabledRefRO<{arg.TypeArgumentFullName}>(index)"; - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) - ); - break; - case QueryType.UnmanagedSharedComponent: - case QueryType.ManagedSharedComponent: - fieldName = $"{arg.Name}"; - fieldDeclaration = $"public {arg.TypeSymbolFullName} {fieldName};"; - elementInReturnedTuple = fieldName; - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) + new ArgumentInReturnedType(default) ); - break; - case QueryType.DynamicBuffer: - fieldName = $"{arg.Name}_BufferAccessor"; - fieldDeclaration = $"public Unity.Entities.BufferAccessor<{arg.TypeArgumentFullName}> {fieldName};"; - elementInReturnedTuple = $"{fieldName}[index]"; - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) - ); - break; - case QueryType.UnityEngineComponent: - fieldName = $"{arg.Name}_ManagedComponentAccessor"; - fieldDeclaration = $"public Unity.Entities.ManagedComponentAccessor<{arg.TypeArgumentFullName}> {fieldName};"; - elementInReturnedTuple = $"new Unity.Entities.SystemAPI.ManagedAPI.UnityEngineComponent<{arg.TypeArgumentFullName}>({fieldName}[index])"; - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) - ); - break; - case QueryType.ManagedComponent: - fieldName = $"{arg.Name}_ManagedComponentAccessor"; - fieldDeclaration = $"public Unity.Entities.ManagedComponentAccessor<{arg.TypeSymbolFullName}> {fieldName};"; - elementInReturnedTuple = $"{fieldName}[index]"; - yield return - ( - new Field(fieldDeclaration, fieldName), - new ArgumentInReturnedType(elementInReturnedTuple) - ); - break; - default: - throw new ArgumentOutOfRangeException(); - } - } + } + + fieldName = $"{arg.Name}_IntPtr"; + fieldDeclaration = $"public global::System.IntPtr {fieldName};"; + elementInReturnedTuple = + performCollectionChecks + ? $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRW<{arg.TypeArgumentFullName}>({fieldName}, index, ref {typeHandleName})" + : $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRW<{arg.TypeArgumentFullName}>({fieldName}, index)" ; - if (provideEntityAccess) - yield return + yield return ( - new Field("public global::System.IntPtr Entity_IntPtr;", "Entity_IntPtr"), - new ArgumentInReturnedType("Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement(Entity_IntPtr, index)") + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) ); - } + break; + case QueryType.EnabledRefRW: + fieldName = $"{arg.Name}_EnabledMask"; + fieldDeclaration = $"public Unity.Entities.EnabledMask {fieldName};"; + elementInReturnedTuple = $"{fieldName}.GetEnabledRefRW<{arg.TypeArgumentFullName}>(index)"; + yield return + ( + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) + ); + break; + case QueryType.RefRO: + string typeHandleName_= $"{arg.Name}_TypeHandle"; - public static IEnumerable<(Field Field, ArgumentInReturnedType ArgumentWhenInitializingResolvedChunk)> - TypeHandle(IReadOnlyCollection elements, bool provideEntityAccess, - bool performsCollectionChecks) - { - foreach (var arg in elements) - { - string fieldName; - string fieldDeclaration; - string fieldAssignment; - string resolvedChunkInitializerArgument; - string initializerArgumentSetUp = default; - - Field field; - - switch (arg.Type) - { - case QueryType.Aspect: - fieldName = $"{arg.Name}_AspectTypeHandle"; - fieldDeclaration = $"{arg.TypeSymbolFullName}.TypeHandle {fieldName};"; - fieldAssignment = $"{fieldName} = new {arg.TypeSymbolFullName}.TypeHandle(ref systemState);"; - resolvedChunkInitializerArgument = $"{fieldName}.Resolve(archetypeChunk);"; + if (performCollectionChecks) + { + fieldName = typeHandleName_; + fieldDeclaration = $"[Unity.Collections.ReadOnly] public Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; yield return ( - new Field( - fieldDeclaration, - fieldName, - fieldAssignment), - new ArgumentInReturnedType(resolvedChunkInitializerArgument) + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(default) ); - break; - case QueryType.TagComponent: - fieldName = ""; - fieldDeclaration = ""; - fieldAssignment = ""; - resolvedChunkInitializerArgument = ""; + } - yield return - ( - new Field( - fieldDeclaration, - fieldName, - fieldAssignment), - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - break; - case QueryType.RefRO_TagComponent: - fieldName = $"{arg.Name}_ComponentTypeHandle_RO"; - fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly: true);"; - - field = new Field(fieldDeclaration, fieldName, fieldAssignment); - - if (performsCollectionChecks) - { - resolvedChunkInitializerArgument = $"{fieldName};"; - yield return - ( - field, - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - } - - resolvedChunkInitializerArgument = - $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtrWithoutChecks<{arg.TypeArgumentFullName}>(archetypeChunk, ref {fieldName});"; + fieldName = $"{arg.Name}_IntPtr"; + fieldDeclaration = $"public global::System.IntPtr {fieldName};"; + elementInReturnedTuple = + performCollectionChecks + ? $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRO<{arg.TypeArgumentFullName}>({fieldName}, index, ref {typeHandleName_})" + : $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRO<{arg.TypeArgumentFullName}>({fieldName}, index)"; - yield return - ( - field, - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - break; - case QueryType.RefRW_TagComponent: - fieldName = $"{arg.Name}_ComponentTypeHandle_RW"; - fieldDeclaration = $"Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly);"; - - field = new Field(fieldDeclaration, fieldName, fieldAssignment); - - if (performsCollectionChecks) - { - resolvedChunkInitializerArgument = $"{fieldName};"; - yield return - ( - field, - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - } - - resolvedChunkInitializerArgument = - $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtrWithoutChecks<{arg.TypeArgumentFullName}>(archetypeChunk, ref {fieldName});"; + yield return + ( + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) + ); + break; + case QueryType.EnabledRefRO: + fieldName = $"{arg.Name}_EnabledMask"; + fieldDeclaration = $"public Unity.Entities.EnabledMask {fieldName};"; + elementInReturnedTuple = $"{fieldName}.GetEnabledRefRO<{arg.TypeArgumentFullName}>(index)"; + yield return + ( + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) + ); + break; + case QueryType.UnmanagedSharedComponent: + case QueryType.ManagedSharedComponent: + fieldName = $"{arg.Name}"; + fieldDeclaration = $"public {arg.TypeSymbolFullName} {fieldName};"; + elementInReturnedTuple = fieldName; + yield return + ( + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) + ); + break; + case QueryType.DynamicBuffer: + fieldName = $"{arg.Name}_BufferAccessor"; + fieldDeclaration = $"public Unity.Entities.BufferAccessor<{arg.TypeArgumentFullName}> {fieldName};"; + elementInReturnedTuple = $"{fieldName}[index]"; + yield return + ( + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) + ); + break; + case QueryType.UnityEngineComponent: + fieldName = $"{arg.Name}_ManagedComponentAccessor"; + fieldDeclaration = $"public Unity.Entities.ManagedComponentAccessor<{arg.TypeArgumentFullName}> {fieldName};"; + elementInReturnedTuple = $"new Unity.Entities.SystemAPI.ManagedAPI.UnityEngineComponent<{arg.TypeArgumentFullName}>({fieldName}[index])"; + yield return + ( + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) + ); + break; + case QueryType.ManagedComponent: + fieldName = $"{arg.Name}_ManagedComponentAccessor"; + fieldDeclaration = $"public Unity.Entities.ManagedComponentAccessor<{arg.TypeSymbolFullName}> {fieldName};"; + elementInReturnedTuple = $"{fieldName}[index]"; + yield return + ( + new Field(fieldDeclaration, fieldName), + new ArgumentInReturnedType(elementInReturnedTuple) + ); + break; + default: + throw new ArgumentOutOfRangeException(); + } + } - yield return - ( - field, - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - break; - case QueryType.ValueTypeComponent: - fieldName = $"{arg.Name}_ComponentTypeHandle_RO"; - fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeSymbolFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeSymbolFullName}>(isReadOnly: true);"; - resolvedChunkInitializerArgument = $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtrWithoutChecks<{arg.TypeSymbolFullName}>(archetypeChunk, ref {fieldName});"; + if (provideEntityAccess) + yield return + ( + new Field("public global::System.IntPtr Entity_IntPtr;", "Entity_IntPtr"), + new ArgumentInReturnedType("Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement(Entity_IntPtr, index)") + ); + } - yield return - ( - new Field( - fieldDeclaration, - fieldName, - fieldAssignment), - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - break; - case QueryType.RefRW: - fieldName = $"{arg.Name}_ComponentTypeHandle_RW"; - fieldDeclaration = $"Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly);"; - - field = new Field(fieldDeclaration, fieldName, fieldAssignment); - if (performsCollectionChecks) - { - resolvedChunkInitializerArgument = $"{fieldName};"; - yield return - ( - field, - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - } - - resolvedChunkInitializerArgument = - $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtrWithoutChecks<{arg.TypeArgumentFullName}>(archetypeChunk, ref {fieldName});"; + public static IEnumerable<(Field Field, ArgumentInReturnedType ArgumentWhenInitializingResolvedChunk)> + TypeHandle(IReadOnlyCollection elements, bool provideEntityAccess, + bool performsCollectionChecks) + { + foreach (var arg in elements) + { + string fieldName; + string fieldDeclaration; + string fieldAssignment; + string resolvedChunkInitializerArgument; + string initializerArgumentSetUp = default; - yield return - ( - field, - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - break; - case QueryType.EnabledRefRW: - fieldName = $"{arg.Name}_ComponentTypeHandle_RW"; - fieldDeclaration = $"Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly);"; - resolvedChunkInitializerArgument = $"archetypeChunk.GetEnabledMask(ref {fieldName});"; + Field field; - yield return - ( - new Field( - fieldDeclaration, - fieldName, - fieldAssignment), - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - break; - case QueryType.RefRO: - fieldName = $"{arg.Name}_ComponentTypeHandle_RO"; - fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly: true);"; - - field = new Field(fieldDeclaration, fieldName, fieldAssignment); - - if (performsCollectionChecks) - { - resolvedChunkInitializerArgument = $"{fieldName};"; - yield return - ( - field, - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - } - - resolvedChunkInitializerArgument = - $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtrWithoutChecks<{arg.TypeArgumentFullName}>(archetypeChunk, ref {fieldName});"; + switch (arg.Type) + { + case QueryType.Aspect: + fieldName = $"{arg.Name}_AspectTypeHandle"; + fieldDeclaration = $"{arg.TypeSymbolFullName}.TypeHandle {fieldName};"; + fieldAssignment = $"{fieldName} = new {arg.TypeSymbolFullName}.TypeHandle(ref systemState);"; + resolvedChunkInitializerArgument = $"{fieldName}.Resolve(archetypeChunk);"; + yield return + ( + new Field( + fieldDeclaration, + fieldName, + fieldAssignment), + new ArgumentInReturnedType(resolvedChunkInitializerArgument) + ); + break; + case QueryType.TagComponent: + fieldName = ""; + fieldDeclaration = ""; + fieldAssignment = ""; + resolvedChunkInitializerArgument = ""; + + yield return + ( + new Field( + fieldDeclaration, + fieldName, + fieldAssignment), + new ArgumentInReturnedType(resolvedChunkInitializerArgument) + ); + break; + case QueryType.ValueTypeComponent: + fieldName = $"{arg.Name}_ComponentTypeHandle_RO"; + fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeSymbolFullName}> {fieldName};"; + fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeSymbolFullName}>(isReadOnly: true);"; + resolvedChunkInitializerArgument = $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtrWithoutChecks<{arg.TypeSymbolFullName}>(archetypeChunk, ref {fieldName});"; + + yield return + ( + new Field( + fieldDeclaration, + fieldName, + fieldAssignment), + new ArgumentInReturnedType(resolvedChunkInitializerArgument) + ); + break; + case QueryType.RefRW: + fieldName = $"{arg.Name}_ComponentTypeHandle_RW"; + fieldDeclaration = $"Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; + fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly);"; + + field = new Field(fieldDeclaration, fieldName, fieldAssignment); + if (performsCollectionChecks) + { + resolvedChunkInitializerArgument = $"{fieldName};"; yield return ( field, new ArgumentInReturnedType(resolvedChunkInitializerArgument) ); - break; - case QueryType.EnabledRefRO: - fieldName = $"{arg.Name}_ComponentTypeHandle_RO"; - fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly: true);"; - resolvedChunkInitializerArgument = $"archetypeChunk.GetEnabledMask(ref {fieldName});"; + } - yield return - ( - new Field( - fieldDeclaration, - fieldName, - fieldAssignment), - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - break; - case QueryType.UnmanagedSharedComponent: - fieldName = $"{arg.Name}_SharedComponentTypeHandle_RO"; - fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.SharedComponentTypeHandle<{arg.TypeSymbolFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.GetSharedComponentTypeHandle<{arg.TypeSymbolFullName}>();"; - resolvedChunkInitializerArgument = $"{arg.Name};"; - initializerArgumentSetUp = $"var {arg.Name} = archetypeChunk.GetSharedComponent({fieldName}, _entityManager);"; + resolvedChunkInitializerArgument = + $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtrWithoutChecks<{arg.TypeArgumentFullName}>(archetypeChunk, ref {fieldName});"; - yield return - ( - new Field( - fieldDeclaration, - fieldName, - fieldAssignment, - dependsOnEntityManagerField: true), - new ArgumentInReturnedType(resolvedChunkInitializerArgument, initializerArgumentSetUp) - ); - break; - case QueryType.ManagedSharedComponent: - fieldName = $"{arg.Name}_SharedComponentTypeHandle_RO"; - fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.SharedComponentTypeHandle<{arg.TypeSymbolFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.GetSharedComponentTypeHandle<{arg.TypeSymbolFullName}>();"; - resolvedChunkInitializerArgument = $"{arg.Name};"; - initializerArgumentSetUp = $"var {arg.Name} = archetypeChunk.GetSharedComponentManaged({fieldName}, _entityManager);"; + yield return + ( + field, + new ArgumentInReturnedType(resolvedChunkInitializerArgument) + ); + break; + case QueryType.EnabledRefRW: + fieldName = $"{arg.Name}_ComponentTypeHandle_RW"; + fieldDeclaration = $"Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; + fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly);"; + resolvedChunkInitializerArgument = $"archetypeChunk.GetEnabledMask(ref {fieldName});"; + + yield return + ( + new Field( + fieldDeclaration, + fieldName, + fieldAssignment), + new ArgumentInReturnedType(resolvedChunkInitializerArgument) + ); + break; + case QueryType.RefRO: + fieldName = $"{arg.Name}_ComponentTypeHandle_RO"; + fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; + fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly: true);"; - yield return - ( - new Field( - fieldDeclaration, - fieldName, - fieldAssignment, - dependsOnEntityManagerField: true), - new ArgumentInReturnedType(resolvedChunkInitializerArgument, initializerArgumentSetUp) - ); - break; - case QueryType.UnityEngineComponent: - fieldName = $"{arg.Name}_ManagedComponentTypeHandle_RO"; - fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.EntityManager.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(true);"; - resolvedChunkInitializerArgument = $"archetypeChunk.GetManagedComponentAccessor(ref {fieldName}, _entityManager);"; + field = new Field(fieldDeclaration, fieldName, fieldAssignment); + if (performsCollectionChecks) + { + resolvedChunkInitializerArgument = $"{fieldName};"; yield return ( - new Field( - fieldDeclaration, - fieldName, - fieldAssignment, - dependsOnEntityManagerField: true), + field, new ArgumentInReturnedType(resolvedChunkInitializerArgument) ); - break; - case QueryType.ManagedComponent: - fieldName = $"{arg.Name}_ManagedComponentTypeHandle_RO"; - fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeSymbolFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.EntityManager.GetComponentTypeHandle<{arg.TypeSymbolFullName}>(true);"; - resolvedChunkInitializerArgument = $"archetypeChunk.GetManagedComponentAccessor(ref {fieldName}, _entityManager);"; + } - yield return - ( - new Field( - fieldDeclaration, - fieldName, - fieldAssignment, - dependsOnEntityManagerField: true), - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - break; - case QueryType.DynamicBuffer: - fieldName = $"{arg.Name}_BufferTypeHandle_RW"; - fieldDeclaration = $"Unity.Entities.BufferTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; - fieldAssignment = $"{fieldName} = systemState.GetBufferTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly);"; - resolvedChunkInitializerArgument = $"archetypeChunk.GetBufferAccessor(ref {fieldName});"; + resolvedChunkInitializerArgument = + $"Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayReadOnlyIntPtrWithoutChecks<{arg.TypeArgumentFullName}>(archetypeChunk, ref {fieldName});"; - yield return - ( - new Field( - fieldDeclaration, - fieldName, - fieldAssignment), - new ArgumentInReturnedType(resolvedChunkInitializerArgument) - ); - break; - default: - throw new ArgumentOutOfRangeException(); - } + yield return + ( + field, + new ArgumentInReturnedType(resolvedChunkInitializerArgument) + ); + break; + case QueryType.EnabledRefRO: + fieldName = $"{arg.Name}_ComponentTypeHandle_RO"; + fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; + fieldAssignment = $"{fieldName} = systemState.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly: true);"; + resolvedChunkInitializerArgument = $"archetypeChunk.GetEnabledMask(ref {fieldName});"; + + yield return + ( + new Field( + fieldDeclaration, + fieldName, + fieldAssignment), + new ArgumentInReturnedType(resolvedChunkInitializerArgument) + ); + break; + case QueryType.UnmanagedSharedComponent: + fieldName = $"{arg.Name}_SharedComponentTypeHandle_RO"; + fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.SharedComponentTypeHandle<{arg.TypeSymbolFullName}> {fieldName};"; + fieldAssignment = $"{fieldName} = systemState.GetSharedComponentTypeHandle<{arg.TypeSymbolFullName}>();"; + resolvedChunkInitializerArgument = $"{arg.Name};"; + initializerArgumentSetUp = $"var {arg.Name} = archetypeChunk.GetSharedComponent({fieldName}, _entityManager);"; + + yield return + ( + new Field( + fieldDeclaration, + fieldName, + fieldAssignment, + dependsOnEntityManagerField: true), + new ArgumentInReturnedType(resolvedChunkInitializerArgument, initializerArgumentSetUp) + ); + break; + case QueryType.ManagedSharedComponent: + fieldName = $"{arg.Name}_SharedComponentTypeHandle_RO"; + fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.SharedComponentTypeHandle<{arg.TypeSymbolFullName}> {fieldName};"; + fieldAssignment = $"{fieldName} = systemState.GetSharedComponentTypeHandle<{arg.TypeSymbolFullName}>();"; + resolvedChunkInitializerArgument = $"{arg.Name};"; + initializerArgumentSetUp = $"var {arg.Name} = archetypeChunk.GetSharedComponentManaged({fieldName}, _entityManager);"; + + yield return + ( + new Field( + fieldDeclaration, + fieldName, + fieldAssignment, + dependsOnEntityManagerField: true), + new ArgumentInReturnedType(resolvedChunkInitializerArgument, initializerArgumentSetUp) + ); + break; + case QueryType.UnityEngineComponent: + fieldName = $"{arg.Name}_ManagedComponentTypeHandle_RO"; + fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; + fieldAssignment = $"{fieldName} = systemState.EntityManager.GetComponentTypeHandle<{arg.TypeArgumentFullName}>(true);"; + resolvedChunkInitializerArgument = $"archetypeChunk.GetManagedComponentAccessor(ref {fieldName}, _entityManager);"; + + yield return + ( + new Field( + fieldDeclaration, + fieldName, + fieldAssignment, + dependsOnEntityManagerField: true), + new ArgumentInReturnedType(resolvedChunkInitializerArgument) + ); + break; + case QueryType.ManagedComponent: + fieldName = $"{arg.Name}_ManagedComponentTypeHandle_RO"; + fieldDeclaration = $"[Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle<{arg.TypeSymbolFullName}> {fieldName};"; + fieldAssignment = $"{fieldName} = systemState.EntityManager.GetComponentTypeHandle<{arg.TypeSymbolFullName}>(true);"; + resolvedChunkInitializerArgument = $"archetypeChunk.GetManagedComponentAccessor(ref {fieldName}, _entityManager);"; + + yield return + ( + new Field( + fieldDeclaration, + fieldName, + fieldAssignment, + dependsOnEntityManagerField: true), + new ArgumentInReturnedType(resolvedChunkInitializerArgument) + ); + break; + case QueryType.DynamicBuffer: + fieldName = $"{arg.Name}_BufferTypeHandle_RW"; + fieldDeclaration = $"Unity.Entities.BufferTypeHandle<{arg.TypeArgumentFullName}> {fieldName};"; + fieldAssignment = $"{fieldName} = systemState.GetBufferTypeHandle<{arg.TypeArgumentFullName}>(isReadOnly);"; + resolvedChunkInitializerArgument = $"archetypeChunk.GetBufferAccessor(ref {fieldName});"; + + yield return + ( + new Field( + fieldDeclaration, + fieldName, + fieldAssignment), + new ArgumentInReturnedType(resolvedChunkInitializerArgument) + ); + break; + default: + throw new ArgumentOutOfRangeException(); } + } - if (provideEntityAccess) - { - var entityField = - new Field( - "Unity.Entities.EntityTypeHandle Entity_TypeHandle;", - "Entity_TypeHandle", - "Entity_TypeHandle = systemState.GetEntityTypeHandle();"); - yield return (entityField, new ArgumentInReturnedType("Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkEntityArrayIntPtr(archetypeChunk, Entity_TypeHandle);")); - } + if (provideEntityAccess) + { + var entityField = + new Field( + "Unity.Entities.EntityTypeHandle Entity_TypeHandle;", + "Entity_TypeHandle", + "Entity_TypeHandle = systemState.GetEntityTypeHandle();"); + yield return (entityField, new ArgumentInReturnedType("Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkEntityArrayIntPtr(archetypeChunk, Entity_TypeHandle);")); } + } - internal static IEnumerable InitializeResolvedChunkInstanceInTypeHandle( - IEnumerable<(Field ResolvedChunkField, ArgumentInReturnedType Argument)> pairedFields) + internal static IEnumerable InitializeResolvedChunkInstanceInTypeHandle( + IEnumerable<(Field ResolvedChunkField, ArgumentInReturnedType Argument)> pairedFields) + { + foreach (var pair in pairedFields) { - foreach (var pair in pairedFields) - { - if (string.IsNullOrEmpty(pair.ResolvedChunkField.Name)) - continue; + if (string.IsNullOrEmpty(pair.ResolvedChunkField.Name)) + continue; - if (pair.Argument.RequiresSetup) - yield return pair.Argument.Setup; + if (pair.Argument.RequiresSetup) + yield return pair.Argument.Setup; - yield return $"resolvedChunk.{pair.ResolvedChunkField.Name} = {pair.Argument.Value}"; - } + yield return $"resolvedChunk.{pair.ResolvedChunkField.Name} = {pair.Argument.Value}"; } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/QueryCandidate.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/QueryCandidate.cs index 642e6b2..887fc12 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/QueryCandidate.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/QueryCandidate.cs @@ -4,33 +4,26 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +public struct QueryCandidate : ISystemCandidate { - public struct QueryCandidate : ISystemCandidate - { - public bool IsContainedInForEachStatement => ContainingStatementNode is CommonForEachStatementSyntax; - public IReadOnlyCollection QueryTypeNodes { get; private set; } - public StatementSyntax ContainingStatementNode { get; private set; } - public SyntaxNode FullInvocationChainSyntaxNode { get; private set; } - public TypeDeclarationSyntax ContainingTypeNode { get; private set; } - public InvocationExpressionSyntax[] MethodInvocationNodes { get; private set; } + public IReadOnlyCollection QueryTypeNodes { get; private set; } + public SyntaxNode FullInvocationChainSyntaxNode { get; private set; } + public TypeDeclarationSyntax ContainingTypeNode { get; private set; } - public static QueryCandidate From( - InvocationExpressionSyntax queryInvocationSyntaxNode, - IReadOnlyCollection queryTypeSyntaxNodes) + public static QueryCandidate From( + InvocationExpressionSyntax fullInvocationChainSyntaxNode, + IReadOnlyCollection queryTypeSyntaxNodes) + { + return new QueryCandidate { - var fullInvocationChainSyntaxNode = queryInvocationSyntaxNode.Ancestors().OfType().LastOrDefault() ?? queryInvocationSyntaxNode; - return new QueryCandidate - { - ContainingStatementNode = queryInvocationSyntaxNode.Ancestors().OfType().FirstOrDefault(), - FullInvocationChainSyntaxNode = fullInvocationChainSyntaxNode, - QueryTypeNodes = queryTypeSyntaxNodes, - ContainingTypeNode = queryInvocationSyntaxNode.Ancestors().OfType().First(), - MethodInvocationNodes = fullInvocationChainSyntaxNode.DescendantNodesAndSelf().OfType().ToArray() - }; - } - - public string CandidateTypeName => "SystemAPI.Query"; - public SyntaxNode Node => FullInvocationChainSyntaxNode; + FullInvocationChainSyntaxNode = fullInvocationChainSyntaxNode, + QueryTypeNodes = queryTypeSyntaxNodes, + ContainingTypeNode = fullInvocationChainSyntaxNode.Ancestors().OfType().First(), + }; } + + public string CandidateTypeName => "SystemAPI.Query"; + public SyntaxNode Node => FullInvocationChainSyntaxNode; } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/QueryType.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/QueryType.cs index cbf8f48..35db471 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/QueryType.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/QueryType.cs @@ -1,21 +1,18 @@ -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +enum QueryType { - enum QueryType - { - RefRW, - RefRO, - RefRW_TagComponent, - RefRO_TagComponent, - UnmanagedSharedComponent, - ManagedSharedComponent, - Aspect, - DynamicBuffer, - ValueTypeComponent, - ManagedComponent, - UnityEngineComponent, - EnabledRefRW, - EnabledRefRO, - TagComponent, - Invalid - } + RefRW, + RefRO, + UnmanagedSharedComponent, + ManagedSharedComponent, + Aspect, + DynamicBuffer, + ValueTypeComponent, + ManagedComponent, + UnityEngineComponent, + EnabledRefRW, + EnabledRefRO, + TagComponent, + Invalid } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/ReturnedTupleElementDuringEnumeration.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/ReturnedTupleElementDuringEnumeration.cs index b0a0644..7dadf8c 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/ReturnedTupleElementDuringEnumeration.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/ReturnedTupleElementDuringEnumeration.cs @@ -1,22 +1,21 @@ -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; + +readonly struct ReturnedTupleElementDuringEnumeration { - readonly struct ReturnedTupleElementDuringEnumeration - { - public readonly string TypeSymbolFullName; - public readonly string TypeArgumentFullName; - public readonly string Name; - public readonly QueryType Type; + public readonly string TypeSymbolFullName; + public readonly string TypeArgumentFullName; + public readonly string Name; + public readonly QueryType Type; - public ReturnedTupleElementDuringEnumeration( - string typeSymbolFullName, - string typeArgumentFullName, - string elementName, - QueryType type) - { - TypeSymbolFullName = typeSymbolFullName; - TypeArgumentFullName = typeArgumentFullName; - Name = elementName; - Type = type; - } + public ReturnedTupleElementDuringEnumeration( + string typeSymbolFullName, + string typeArgumentFullName, + string elementName, + QueryType type) + { + TypeSymbolFullName = typeSymbolFullName; + TypeArgumentFullName = typeArgumentFullName; + Name = elementName; + Type = type; } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.csproj b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.csproj index 21fdc69..245b6c5 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.csproj +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.Query/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.csproj @@ -3,7 +3,9 @@ true netstandard2.0 - 8.0 + latest + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/QueryCandidate.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/QueryCandidate.cs index 5868f32..f0d9462 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/QueryCandidate.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/QueryCandidate.cs @@ -35,8 +35,10 @@ public static (bool Success, QueryCandidate Result) TryCreateFrom(InvocationExpr }); bool IsBuildNode(InvocationExpressionSyntax invocationExpressionSyntax) => - invocationExpressionSyntax.Expression is MemberAccessExpressionSyntax memberAccessExpressionSyntax - && memberAccessExpressionSyntax.Name.Identifier.ValueText == "Build"; + invocationExpressionSyntax.Expression is MemberAccessExpressionSyntax { Name: { Identifier: + { + ValueText: "Build" + } } }; } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderDescription.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderDescription.cs index ef5ed8e..72ef05f 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderDescription.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderDescription.cs @@ -2,7 +2,6 @@ using System.Linq; using System.Text; using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Operations; using Unity.Entities.SourceGen.Common; @@ -10,9 +9,8 @@ namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder { - public class SystemAPIQueryBuilderDescription + public class SystemApiQueryBuilderDescription { - readonly QueryCandidate _queryCandidate; readonly StringBuilder _invocationsBeforeBuild = new StringBuilder(); public readonly bool IsBurstEnabled; @@ -20,9 +18,6 @@ public class SystemAPIQueryBuilderDescription public readonly List QueryFinalizingLocations = new List(); public readonly SystemDescription SystemDescription; - public SyntaxNode NodeToReplace => _queryCandidate.BuildNode; - public SyntaxNode GetReplacementNode() => SyntaxFactory.IdentifierName(GeneratedEntityQueryFieldName); - public string GeneratedEntityQueryFieldName { get; set; } public bool Success { get; set; } = true; @@ -41,7 +36,7 @@ so that we can use the above when generating */ public string GetQueryBuilderBodyBeforeBuild() => _invocationsBeforeBuild.ToString(); - public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, QueryCandidate queryCandidate) + public SystemApiQueryBuilderDescription(SystemDescription systemDescription, QueryCandidate queryCandidate) { SystemDescription = systemDescription; @@ -54,15 +49,13 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que if (systemDescription.SemanticModel.GetOperation(queryCandidate.BuildNode) is IInvocationOperation invocationOperation && invocationOperation.TargetMethod.ToString() == "Unity.Entities.SystemAPIQueryBuilder.Build()") { - _queryCandidate = queryCandidate; - SystemDescription = systemDescription; queryCandidate.BuildNode.GetLocation(); var containingMethod = queryCandidate.BuildNode.AncestorOfKindOrDefault(); if (containingMethod != null) { - var methodSymbol = (IMethodSymbol)ModelExtensions.GetDeclaredSymbol(SystemDescription.SemanticModel, containingMethod); + var methodSymbol = (IMethodSymbol)SystemDescription.SemanticModel.GetDeclaredSymbol(containingMethod); IsBurstEnabled = methodSymbol.HasAttribute("Unity.Burst.BurstCompileAttribute"); var noneQueryTypes = new List(); @@ -70,6 +63,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que var allQueryTypes = new List(); var disabledQueryTypes = new List(); var absentQueryTypes = new List(); + var presentQueryTypes = new List(); var entityQueryOptions = new List(); foreach (var node in queryCandidate.SystemAPIQueryBuilderNode.Ancestors().OfType()) @@ -86,7 +80,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.Disabled, IsReadOnly = true }).ToArray(); @@ -99,7 +93,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.Disabled, IsReadOnly = false }).ToArray(); @@ -107,12 +101,37 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que _invocationsBeforeBuild.AppendLine($".WithDisabledRW<{typeArguments.Select(t => t.TypeSymbol.ToFullName()).SeparateByCommaAndSpace()}>()"); disabledQueryTypes.AddRange(typeArguments); break; + case "WithPresent": + typeArguments = + genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => + new Query + { + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, + Type = QueryType.Present, + IsReadOnly = true + }).ToArray(); + + _invocationsBeforeBuild.AppendLine($".WithPresent<{typeArguments.Select(t => t.TypeSymbol.ToFullName()).SeparateByCommaAndSpace()}>()"); + presentQueryTypes.AddRange(typeArguments); + break; + case "WithPresentRW": + typeArguments = + genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => + new Query + { + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, + Type = QueryType.Present, + IsReadOnly = false + }).ToArray(); + _invocationsBeforeBuild.AppendLine($".WithPresentRW<{typeArguments.Select(t => t.TypeSymbol.ToFullName()).SeparateByCommaAndSpace()}>()"); + presentQueryTypes.AddRange(typeArguments); + break; case "WithAbsent": typeArguments = genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.Absent, IsReadOnly = true }).ToArray(); @@ -125,8 +144,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions - .GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.Absent, IsReadOnly = true }).ToArray(); @@ -139,7 +157,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.All, IsReadOnly = true }).ToArray(); @@ -152,8 +170,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions - .GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.All, IsReadOnly = false }).ToArray(); @@ -166,8 +183,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions - .GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.All, IsReadOnly = true }).ToArray(); @@ -180,8 +196,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions - .GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.All, IsReadOnly = false }).ToArray(); @@ -194,8 +209,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions - .GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.Any, IsReadOnly = true }).ToArray(); @@ -207,7 +221,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que typeArguments = genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.Any, IsReadOnly = true }).ToArray(); @@ -219,7 +233,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que typeArguments = genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.Any, IsReadOnly = false }).ToArray(); @@ -231,7 +245,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que typeArguments = genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.Any, IsReadOnly = false }).ToArray(); @@ -244,8 +258,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions - .GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.None, IsReadOnly = true }).ToArray(); @@ -258,8 +271,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions - .GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.None, IsReadOnly = true }).ToArray(); @@ -273,7 +285,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.All, IsReadOnly = false }).ToArray(); @@ -287,7 +299,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que genericNameSyntax.TypeArgumentList.Arguments.Select(typeArg => new Query { - TypeSymbol = (ITypeSymbol)ModelExtensions.GetSymbolInfo(systemDescription.SemanticModel, typeArg).Symbol, + TypeSymbol = systemDescription.SemanticModel.GetTypeInfo(typeArg).Type, Type = QueryType.All, IsReadOnly = true }).ToArray(); @@ -316,12 +328,12 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que if (entityQueryOptions.Count > 1) { - SystemAPIQueryBuilderErrors.SGQB001(SystemDescription, node.GetLocation()); + SystemApiQueryBuilderErrors.SGQB001(SystemDescription, node.GetLocation()); Success = false; } else { - archetype = new Archetype(allQueryTypes, anyQueryTypes, noneQueryTypes, disabledQueryTypes, absentQueryTypes, entityQueryOptions.Any() ? entityQueryOptions.SingleOrDefault() : EntityQueryOptions.Default); + archetype = new Archetype(allQueryTypes, anyQueryTypes, noneQueryTypes, disabledQueryTypes, absentQueryTypes, presentQueryTypes, entityQueryOptions.Any() ? entityQueryOptions.SingleOrDefault() : EntityQueryOptions.Default); Archetypes.Add(archetype); QueryFinalizingLocations.Add(node.GetLocation()); } @@ -334,12 +346,12 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que case "Build": if (entityQueryOptions.Count > 1) { - SystemAPIQueryBuilderErrors.SGQB001(SystemDescription, node.GetLocation()); + SystemApiQueryBuilderErrors.SGQB001(SystemDescription, node.GetLocation()); Success = false; } else { - archetype = new Archetype(allQueryTypes, anyQueryTypes, noneQueryTypes, disabledQueryTypes, absentQueryTypes, entityQueryOptions.Any() ? entityQueryOptions.SingleOrDefault() : EntityQueryOptions.Default); + archetype = new Archetype(allQueryTypes, anyQueryTypes, noneQueryTypes, disabledQueryTypes, absentQueryTypes, presentQueryTypes, entityQueryOptions.Any() ? entityQueryOptions.SingleOrDefault() : EntityQueryOptions.Default); Archetypes.Add(archetype); QueryFinalizingLocations.Add(node.GetLocation()); } @@ -372,7 +384,7 @@ public SystemAPIQueryBuilderDescription(SystemDescription systemDescription, Que if (SourceGenHelpers.TryParseQualifiedEnumValue(argumentExpression.ToString(), out EntityQueryOptions option)) options |= option; - return (options, options.GetFlags().Select(flag => $"Unity.Entities.EntityQueryOptions.{flag.ToString()}").SeparateByBinaryOr()); + return (options, options.GetAsFlagStringSeperatedByOr()); } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderErrors.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderErrors.cs index 09b0751..7d2c166 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderErrors.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderErrors.cs @@ -1,9 +1,10 @@ using Microsoft.CodeAnalysis; +using Unity.Entities.SourceGen.Common; using Unity.Entities.SourceGen.SystemGenerator.Common; namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder { - public static class SystemAPIQueryBuilderErrors + public static class SystemApiQueryBuilderErrors { private const string ErrorTitle = "SystemAPIQueryBuilderError"; diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderModule.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderModule.cs index c893bdf..b6f5956 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderModule.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderModule.cs @@ -7,7 +7,7 @@ namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder { - public class SystemAPIQueryBuilderModule : ISystemModule + public class SystemApiQueryBuilderModule : ISystemModule { private readonly List _queryCandidates = new List(); private Dictionary _candidatesGroupedByContainingSystemTypes; @@ -32,7 +32,7 @@ private Dictionary CandidatesGroupedByC public bool RequiresReferenceToBurst { get; private set; } - public void OnReceiveSyntaxNode(SyntaxNode node) + public void OnReceiveSyntaxNode(SyntaxNode node, Dictionary candidateOwnership) { if (node is InvocationExpressionSyntax invocationExpressionSyntax) { @@ -63,14 +63,16 @@ public void OnReceiveSyntaxNode(SyntaxNode node) } } - readonly string[] _groupNames = { "WithAll", "WithAny", "WithNone", "WithDisabled", "WithAbsent" }; + readonly string[] _groupNames = { "WithAll", "WithAny", "WithNone", "WithDisabled", "WithAbsent", "WithPresent" }; public bool RegisterChangesInSystem(SystemDescription systemDescription) { - var systemApiQueryBuilderDescriptions = new List(); + var systemApiQueryBuilderDescriptions = new List(); + var systemApiQueryBuilderDescriptionGroupedByBuildNodes = new Dictionary(); + foreach (var queryCandidate in CandidatesGroupedByContainingSystemTypes[systemDescription.SystemTypeSyntax]) { - var description = new SystemAPIQueryBuilderDescription(systemDescription, queryCandidate); + var description = new SystemApiQueryBuilderDescription(systemDescription, queryCandidate); foreach (var archetypeInfo in description.Archetypes.Zip(description.QueryFinalizingLocations, (a, l) => (Archetype: a, Location: l))) { @@ -81,6 +83,7 @@ public bool RegisterChangesInSystem(SystemDescription systemDescription) archetypeInfo.Archetype.None, archetypeInfo.Archetype.Disabled, archetypeInfo.Archetype.Absent, + archetypeInfo.Archetype.Present }; for (int i = 0; i < _groupNames.Length; ++i) @@ -91,6 +94,7 @@ public bool RegisterChangesInSystem(SystemDescription systemDescription) archetypeInfo.Location, groupLists[i], invokedMethodName: _groupNames[i]); + for (int j = i + 1; j < _groupNames.Length; ++j) { description.Success &= @@ -112,14 +116,18 @@ public bool RegisterChangesInSystem(SystemDescription systemDescription) RequiresReferenceToBurst = true; systemApiQueryBuilderDescriptions.Add(description); + systemApiQueryBuilderDescriptionGroupedByBuildNodes.Add(queryCandidate.BuildNode, description); + systemDescription.CandidateNodes.Add(queryCandidate.BuildNode, new CandidateSyntax(CandidateType.QueryBuilder, CandidateFlags.None, queryCandidate.BuildNode)); } + foreach (var description in systemApiQueryBuilderDescriptions) { description.GeneratedEntityQueryFieldName - = systemDescription.HandlesDescription.GetOrCreateQueryField(new MultipleArchetypeQueryFieldDescription(description.Archetypes.ToArray(), description.GetQueryBuilderBodyBeforeBuild())); + = systemDescription.QueriesAndHandles.GetOrCreateQueryField( + new MultipleArchetypeQueryFieldDescription(description.Archetypes.ToArray(), + description.GetQueryBuilderBodyBeforeBuild())); } - - systemDescription.Rewriters.Add(new SystemAPIQueryBuilderRewriter(systemApiQueryBuilderDescriptions)); + systemDescription.SyntaxWalkers.Add(Module.SystemApiQueryBuilder, new SystemApiQueryBuilderSyntaxWalker(systemApiQueryBuilderDescriptionGroupedByBuildNodes)); return true; } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderRewriter.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderRewriter.cs deleted file mode 100644 index fa7ffbc..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemAPIQueryBuilderRewriter.cs +++ /dev/null @@ -1,65 +0,0 @@ -using System.Collections.Generic; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.SystemGenerator.Common; - -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder -{ - public class SystemAPIQueryBuilderRewriter : SystemRewriter - { - private readonly IReadOnlyCollection _systemApiQueryBuilderDescriptions; - private readonly Dictionary _queryBuilderInvocationNodeToDescription; - private bool _replacedNode; - - public override IEnumerable NodesToTrack => - _systemApiQueryBuilderDescriptions.Select(d => d.NodeToReplace); - - public SystemAPIQueryBuilderRewriter(IReadOnlyCollection systemApiQueryBuilderDescriptions) - { - _systemApiQueryBuilderDescriptions = systemApiQueryBuilderDescriptions; - _queryBuilderInvocationNodeToDescription = new Dictionary(); - } - - - // By the time this method is invoked, the system has already been rewritten at least once. - // In other words, the `systemRootNode` argument passed to this method is the root node of the REWRITTEN system -- - // i.e., a copy of the original system with changes applied. - public override SyntaxNode VisitTrackedSystem(SyntaxNode systemRootNode, string originalFilePath) - { - m_OriginalFilePath = originalFilePath; - - foreach (var description in _systemApiQueryBuilderDescriptions) - { - var rewrittenQueryInvocationNode = systemRootNode.GetCurrentNodes(description.NodeToReplace).FirstOrDefault() ?? description.NodeToReplace; - _queryBuilderInvocationNodeToDescription.Add(rewrittenQueryInvocationNode, description); - } - return Visit(systemRootNode); - } - - public override SyntaxNode Visit(SyntaxNode syntaxNode) - { - if (syntaxNode == null) - return null; - - var replacedNodeAndChildren = base.Visit(syntaxNode); - - // If the current node is a node we want to replace - if (_queryBuilderInvocationNodeToDescription.TryGetValue(syntaxNode, out var description)) - { - // Replace the current node - replacedNodeAndChildren = description.GetReplacementNode(); - _replacedNode = true; - } - - // If we have performed any replacements, we need to update the `RewrittenMemberHashCodeToSyntaxNode` dictionary accordingly - else if (replacedNodeAndChildren is MemberDeclarationSyntax memberDeclarationSyntax && _replacedNode) - { - RecordChangedMember(memberDeclarationSyntax); - _replacedNode = false; - } - - return replacedNodeAndChildren; - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemApiQueryBuilderSyntaxWalker.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemApiQueryBuilderSyntaxWalker.cs new file mode 100644 index 0000000..6152b37 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/SystemApiQueryBuilderSyntaxWalker.cs @@ -0,0 +1,54 @@ +using System.CodeDom.Compiler; +using System.Collections.Generic; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder +{ +/* + The `QueryBuilderWalker` traverses through syntax nodes that have been marked by the `QueryBuilderModule` as candidates for patching. + It is much more straightforward than the `SystemApiWalker`, since it does not need to handle nested candidates. For illustration purposes, + let's use `var query = SystemAPI.QueryBuilder().WithAspect().Build();` as an example. + + The `SystemSyntaxWalker` walks the method that contains the line above. When it reaches the `InvocationExpressionSyntax` node + `SystemAPI.QueryBuilder().WithAspect().Build()`, which has been marked by the `QueryBuilderModule` as a candidate for patching, the `SystemSyntaxWalker` + cedes write control to the `QueryBuilderWalker` by calling `QueryBuilderWalker.TryWriteSyntax()`. The `QueryBuilderWalker` appends `__generatedAndCachedInSystemQuery`, + and then returns control to the `SystemSyntaxWalker`. The end result is that the `SystemSyntaxWalker` writes the following code: + + var query = __generatedAndCachedInSystemQuery; + */ + public class SystemApiQueryBuilderSyntaxWalker : CSharpSyntaxWalker, IModuleSyntaxWalker + { + private readonly Dictionary _descriptionsGroupedByNodesToReplace; + + private IndentedTextWriter _writer; + private bool _hasWrittenSyntax; + + public SystemApiQueryBuilderSyntaxWalker(Dictionary descriptionsGroupedByNodesToReplace) : base(SyntaxWalkerDepth.Trivia) + => _descriptionsGroupedByNodesToReplace = descriptionsGroupedByNodesToReplace; + + public bool TryWriteSyntax(IndentedTextWriter writer, CandidateSyntax candidateSyntax) + { + _writer = writer; + _hasWrittenSyntax = false; + + // Begin depth-first traversal of the candidate node + Visit(candidateSyntax.Node); + + return _hasWrittenSyntax; + } + + public override void VisitInvocationExpression(InvocationExpressionSyntax node) + { + if (_descriptionsGroupedByNodesToReplace.TryGetValue(node, out var description)) + { + _writer.Write($" {description.GeneratedEntityQueryFieldName}"); + _hasWrittenSyntax = true; + } + else + _hasWrittenSyntax = false; + } + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.csproj b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.csproj index eccb1b9..af1f6c8 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.csproj +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI.QueryBuilder/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.csproj @@ -6,6 +6,8 @@ disable disable 8 + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemAPIErrors.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemAPIErrors.cs deleted file mode 100644 index 03882c8..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemAPIErrors.cs +++ /dev/null @@ -1,20 +0,0 @@ -using Unity.Entities.SourceGen.SystemGenerator.Common; - -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI -{ - public static class SystemAPIErrors { - private const string k_SystemAPIGenericAccess = "SystemAPI usage with Generic Access"; - const string k_SystemAPIComponentAccess = "SystemAPI usage with Component Access"; - public static void SGSA0001(ISourceGeneratorDiagnosable systemDescription, SystemContextSystemModule.CandidateSyntax candidate) { - systemDescription.LogError(nameof(SGSA0001), k_SystemAPIGenericAccess, - "SystemAPI usage with generic parameter not supported", candidate.Node.GetLocation()); - } - - public static void SGSA0002(ISourceGeneratorDiagnosable systemDescription, SystemContextSystemModule.CandidateSyntax candidate) { - var dataLookupName = candidate.Type.ToString(); - systemDescription.LogError(nameof(SGSA0002), k_SystemAPIComponentAccess, - $"{dataLookupName} usage with variable read permission not supported outside OnCreate of your system. To fix, instead call it inside OnCreate of your calling system", - candidate.Node.GetLocation()); - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemApiContextErrors.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemApiContextErrors.cs new file mode 100644 index 0000000..3128466 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemApiContextErrors.cs @@ -0,0 +1,25 @@ +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI; + +public static class SystemApiContextErrors +{ + const string k_SystemAPIGenericAccess = "SystemAPI usage with Generic Access"; + const string k_SystemAPIComponentAccess = "SystemAPI usage with Component Access"; + + public static void SGSA0001(ISourceGeneratorDiagnosable systemDescription, CandidateSyntax candidate) + { + systemDescription.LogError(nameof(SGSA0001), k_SystemAPIGenericAccess, + "SystemAPI usage with generic parameter not supported", candidate.Node.GetLocation()); + } + + public static void SGSA0002(ISourceGeneratorDiagnosable systemDescription, CandidateSyntax candidate) + { + var dataLookupName = candidate.Type.ToString(); + + systemDescription.LogError(nameof(SGSA0002), k_SystemAPIComponentAccess, + $"{dataLookupName} usage with variable read permission not supported outside OnCreate of your system. To fix, instead call it inside OnCreate of your calling system", + candidate.Node.GetLocation()); + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemApiContextSyntaxWalker.CodeReplacements.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemApiContextSyntaxWalker.CodeReplacements.cs new file mode 100644 index 0000000..17b7bb1 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemApiContextSyntaxWalker.CodeReplacements.cs @@ -0,0 +1,779 @@ +using System; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI; + +public partial class SystemApiContextSyntaxWalker +{ + private enum ReplacedWith + { + NotReplaced, + InvocationWithMissingArgumentList, + InvocationWithFullArgumentList, + InvocationWithMissingSystemApiArguments + } + + private string TryGetSystemApiTimeReplacementCode(CandidateSyntax candidateSyntax) + { + var semanticModel = _systemDescription.SemanticModel; + + var resolveCandidateSymbol = semanticModel.GetSymbolInfo(candidateSyntax.Node); + var nodeSymbol = resolveCandidateSymbol.Symbol ?? resolveCandidateSymbol.CandidateSymbols.FirstOrDefault(); + var parentTypeInfo = nodeSymbol?.ContainingType; + var isSystemApi = parentTypeInfo?.ToFullName() == "global::Unity.Entities.SystemAPI"; + + if (!isSystemApi) + return default; + + return _systemDescription.TryGetSystemStateParameterName(candidateSyntax, out var systemStateExpression) + ? $"{systemStateExpression}.WorldUnmanaged.Time" + : null; + } + + private (string Replacement, + ReplacedWith ReplacedWith, + ArgumentSyntax ArgumentThatMightInvolveSystemApiInvocation1, + ArgumentSyntax ArgumentThatMightInvolveSystemApiInvocation2) + TryGetReplacementCode(InvocationExpressionSyntax invocationExpressionSyntax, CandidateSyntax candidateSyntax) + { + var semanticModel = _systemDescription.SemanticModel; + + var resolveCandidateSymbol = semanticModel.GetSymbolInfo(candidateSyntax.Node); + var nodeSymbol = resolveCandidateSymbol.Symbol ?? resolveCandidateSymbol.CandidateSymbols.FirstOrDefault(); + var parentTypeInfo = nodeSymbol?.ContainingType; + + var fullName = parentTypeInfo?.ToFullName(); + var isSystemApi = fullName == "global::Unity.Entities.SystemAPI"; + var isManagedApi = fullName == "global::Unity.Entities.SystemAPI.ManagedAPI"; + + if (!isSystemApi && !isManagedApi && !(candidateSyntax.Type == CandidateType.Singleton && + parentTypeInfo.Is("Unity.Entities.ComponentSystemBase"))) + return default; + + switch (nodeSymbol) + { + // No type argument (EntityStorageInfoLookup, Exists) + case IMethodSymbol { TypeArguments.Length: 0 }: + { + switch (candidateSyntax.Type) + { + case CandidateType.GetEntityStorageInfoLookup: + { + var storageInfoLookupField = _systemDescription.QueriesAndHandles + .GetOrCreateEntityStorageInfoLookupField(); + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, out var systemState)) + return default; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetEntityStorageInfoLookup(ref __TypeHandle.{storageInfoLookupField}, ref {systemState})", + ReplacedWith.InvocationWithFullArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default + ); + } + + case CandidateType.Exists: + { + var storageInfoLookupField = _systemDescription.QueriesAndHandles + .GetOrCreateEntityStorageInfoLookupField(); + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, out var systemState)) + return default; + + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.DoesEntityExist(ref __TypeHandle.{storageInfoLookupField}, ref {systemState}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default + ); + } + + case CandidateType.EntityTypeHandle: + { + var typeHandleField = + _systemDescription.QueriesAndHandles.GetOrCreateEntityTypeHandleField(); + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetEntityTypeHandle(ref __TypeHandle.{typeHandleField}, ref {systemStateExpression})", + ReplacedWith.InvocationWithFullArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + } + + break; + } + + // Based on type argument + case IMethodSymbol { TypeArguments.Length: 1 } namedTypeSymbolWithTypeArg: + { + var typeArgument = namedTypeSymbolWithTypeArg.TypeArguments.Single(); + + if (TryGetSystemBaseGeneric(out string replacementCode, out ReplacedWith replacedWith)) + return ( + replacementCode, + replacedWith, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + + switch (candidateSyntax.Type) + { + case CandidateType.GetComponentLookup: + { + var @readonly = false; + var args = invocationExpressionSyntax.ArgumentList.Arguments.ToArray(); + if (args.Length == 0 || bool.TryParse(args[0].Expression.ToString(), out @readonly)) + { + var lookup = + _systemDescription.QueriesAndHandles.GetOrCreateComponentLookupField(typeArgument, + @readonly); + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetComponentLookup<{typeArgument.ToFullName()}>(ref __TypeHandle.{lookup}, ref {systemStateExpression})", + ReplacedWith.InvocationWithFullArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + + var methodDeclarationSyntax = candidateSyntax.Node.AncestorOfKind(); + if (methodDeclarationSyntax.Identifier.ValueText == "OnCreate") + { + var containingMethodSymbol = semanticModel.GetDeclaredSymbol(methodDeclarationSyntax); + if (containingMethodSymbol.Parameters.Length == 0 || + (containingMethodSymbol.Parameters.Length == 1 && containingMethodSymbol.Parameters[0] + .Type.Is("global::Unity.Entities.SystemState"))) + { + _systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression); // Ok to not handle as you can't be in OnCreate without it. By definition of above SystemState constraint. + return ( + $"{systemStateExpression}.{CandidateSyntax.GetSimpleName(candidateSyntax.Node)}({invocationExpressionSyntax.ArgumentList.ToFullString()})", + ReplacedWith.InvocationWithFullArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + } + + SystemApiContextErrors.SGSA0002(_systemDescription, candidateSyntax); + break; + } + case CandidateType.GetComponent when isManagedApi: + { + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + var typeArg = candidateSyntax.Node.DescendantNodes().OfType().Single() + .TypeArgumentList.Arguments.Single(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ($"{systemStateExpression}.EntityManager.GetComponentObject<{typeArg}>(", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + case CandidateType.GetComponent: + { + var lookup = + _systemDescription.QueriesAndHandles.GetOrCreateComponentLookupField(typeArgument, true); + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetComponentAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{lookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + case CandidateType.GetComponentRO: + { + var lookup = + _systemDescription.QueriesAndHandles.GetOrCreateComponentLookupField(typeArgument, true); + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetComponentROAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{lookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + case CandidateType.GetComponentRW: + { + var lookup = + _systemDescription.QueriesAndHandles + .GetOrCreateComponentLookupField(typeArgument, false); + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetComponentRWAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{lookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + case CandidateType.SetComponent: + { + var args = invocationExpressionSyntax.ArgumentList.Arguments.ToArray(); + + if (args.Length != 2) + return default; + + var (entityArg, componentArg) = + args[0].NameColon?.Name.Identifier.ValueText == "component" + ? (args[1], args[0]) + : (args[0], args[1]); + + typeArgument = typeArgument.TypeKind == TypeKind.TypeParameter + ? semanticModel.GetTypeInfo(componentArg.Expression).Type + : typeArgument; + + var lookup = + _systemDescription.QueriesAndHandles + .GetOrCreateComponentLookupField(typeArgument, false); + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.SetComponentAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{lookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: componentArg, + ArgumentThatMightInvolveSystemApiInvocation2: entityArg); + } + case CandidateType.HasComponent when isManagedApi: + { + var typeArg = candidateSyntax.Node.DescendantNodes().OfType().Single() + .TypeArgumentList.Arguments.Single(); + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return _systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression) + ? ($"{systemStateExpression}.EntityManager.HasComponent<{typeArg}>(", + ReplacedWith: ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default) + : default; + } + case CandidateType.HasComponent: + { + var lookup = + _systemDescription.QueriesAndHandles.GetOrCreateComponentLookupField(typeArgument, true); + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.HasComponentAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{lookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + case CandidateType.IsComponentEnabled when isManagedApi: + { + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var typeArg = candidateSyntax.Node.DescendantNodes().OfType().Single() + .TypeArgumentList.Arguments.Single(); + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ($"{systemStateExpression}.EntityManager.IsComponentEnabled<{typeArg}>(", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + case CandidateType.IsComponentEnabled: + { + var lookup = + _systemDescription.QueriesAndHandles.GetOrCreateComponentLookupField(typeArgument, true); + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return + ($"global::Unity.Entities.Internal.InternalCompilerInterface.IsComponentEnabledAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{lookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + case CandidateType.SetComponentEnabled: + { + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var args = invocationExpressionSyntax.ArgumentList.Arguments.ToArray(); + + if (args.Length != 2) + return default; + + var (entityArg, enabledArg) = + args[0].NameColon?.Name.Identifier.ValueText == "value" + ? (args[1], args[0]) + : (args[0], args[1]); + + if (isSystemApi) + { + var lookup = + _systemDescription.QueriesAndHandles.GetOrCreateComponentLookupField(typeArgument, + false); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.SetComponentEnabledAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{lookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: enabledArg); + } + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + var typeArg = candidateSyntax.Node.DescendantNodes().OfType().First() + .TypeArgumentList; + return ($"{systemStateExpression}.EntityManager.SetComponentEnabled{typeArg}(", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: enabledArg); + } + + // Buffer + case CandidateType.GetBufferLookup: + { + var @readonly = false; + var args = invocationExpressionSyntax.ArgumentList.Arguments.ToArray(); + if (args.Length == 0 || bool.TryParse(args[0].Expression.ToString(), out @readonly)) + { + var bufferLookup = + _systemDescription.QueriesAndHandles.GetOrCreateBufferLookupField(typeArgument, + @readonly); + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + return + ($"global::Unity.Entities.Internal.InternalCompilerInterface.GetBufferLookup<{typeArgument.ToFullName()}>(ref __TypeHandle.{bufferLookup}, ref {systemStateExpression})", + ReplacedWith.InvocationWithFullArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + + var methodDeclarationSyntax = candidateSyntax.Node.AncestorOfKind(); + if (methodDeclarationSyntax.Identifier.ValueText == "OnCreate") + { + var containingMethodSymbol = semanticModel.GetDeclaredSymbol(methodDeclarationSyntax); + if (containingMethodSymbol.Parameters.Length == 0 || + (containingMethodSymbol.Parameters.Length == 1 && containingMethodSymbol.Parameters[0] + .Type.Is("Unity.Entities.SystemState"))) + { + _systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression); + return ( + $"{systemStateExpression}.{CandidateSyntax.GetSimpleName(candidateSyntax.Node)}", + ReplacedWith.InvocationWithMissingArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + } + + SystemApiContextErrors.SGSA0002(_systemDescription, candidateSyntax); + break; + } + case CandidateType.GetBuffer: + { + var bufferLookup = + _systemDescription.QueriesAndHandles.GetOrCreateBufferLookupField(typeArgument, false); + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetBufferAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{bufferLookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + case CandidateType.HasBuffer: + { + var bufferLookup = + _systemDescription.QueriesAndHandles.GetOrCreateBufferLookupField(typeArgument, true); + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return + ($"global::Unity.Entities.Internal.InternalCompilerInterface.HasBufferAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{bufferLookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + case CandidateType.IsBufferEnabled: + { + var bufferLookup = + _systemDescription.QueriesAndHandles.GetOrCreateBufferLookupField(typeArgument, true); + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.Single(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.IsBufferEnabledAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{bufferLookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + case CandidateType.SetBufferEnabled: + { + var bufferLookup = + _systemDescription.QueriesAndHandles.GetOrCreateBufferLookupField(typeArgument, false); + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var args = invocationExpressionSyntax.ArgumentList.Arguments.ToArray(); + + if (args.Length != 2) + return default; + + var (entityArg, enabledArg) = + args[0].NameColon?.Name.Identifier.ValueText == "value" + ? (args[1], args[0]) + : (args[0], args[1]); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.SetBufferEnabledAfterCompletingDependency<{typeArgument.ToFullName()}>(ref __TypeHandle.{bufferLookup}, ref {systemStateExpression}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: enabledArg); + } + + // Singleton + case CandidateType.Singleton: + { + var queryFieldName = _systemDescription.QueriesAndHandles + .GetOrCreateQueryField( + new SingleArchetypeQueryFieldDescription( + new Archetype( + new[] + { + new Query + { + IsReadOnly = + (candidateSyntax.Flags & CandidateFlags.ReadOnly) == + CandidateFlags.ReadOnly, + Type = QueryType.All, + TypeSymbol = typeArgument + } + }, + Array.Empty(), + Array.Empty(), + Array.Empty(), + Array.Empty(), + Array.Empty(), + EntityQueryOptions.Default | EntityQueryOptions.IncludeSystems) + )); + + var sn = CandidateSyntax.GetSimpleName(candidateSyntax.Node); + var noGenericGeneration = (candidateSyntax.Flags & CandidateFlags.NoGenericGeneration) == + CandidateFlags.NoGenericGeneration; + var memberAccess = + noGenericGeneration + ? sn.Identifier.ValueText + : sn.ToString(); // e.g. GetSingletonEntity -> query.GetSingletonEntity (with no generic) + + return ($"{queryFieldName}.{memberAccess}", + ReplacedWith.InvocationWithMissingArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + + // Aspect + case CandidateType.Aspect: + { + var @readonly = candidateSyntax.Flags == CandidateFlags.ReadOnly; + + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + var entityArg = invocationExpressionSyntax.ArgumentList.Arguments.First(); + var aspectLookup = + _systemDescription.QueriesAndHandles.GetOrCreateAspectLookup(typeArgument, @readonly); + + var typeFullName = typeArgument.ToFullName(); + + // Because we are partially patching the node with an open parenthesis with no accompanying closing parenthesis, we need to increment `_numClosingBracketsForNestedSystemApiInvocations` by one. + _numClosingBracketsForNestedSystemApiInvocations++; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetAspectAfterCompletingDependency<{typeFullName}.Lookup, {typeFullName}>(ref __TypeHandle.{aspectLookup}, ref {systemStateExpression}, {(@readonly ? "true" : "false")}, ", + ReplacedWith.InvocationWithMissingSystemApiArguments, + ArgumentThatMightInvolveSystemApiInvocation1: entityArg, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + + // TypeHandle + case CandidateType.ComponentTypeHandle: + { + var @readonly = false; + var args = invocationExpressionSyntax.ArgumentList.Arguments.ToArray(); + if (args.Length == 0 || bool.TryParse(args[0].Expression.ToString(), out @readonly)) + { + var result = + _systemDescription.QueriesAndHandles.GetOrCreateTypeHandleField(typeArgument, + @readonly); + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetComponentTypeHandle(ref __TypeHandle.{result}, ref {systemStateExpression})", + ReplacedWith.InvocationWithFullArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + + var methodDeclarationSyntax = candidateSyntax.Node.AncestorOfKind(); + if (methodDeclarationSyntax.Identifier.ValueText == "OnCreate") + { + var containingMethodSymbol = semanticModel.GetDeclaredSymbol(methodDeclarationSyntax); + if (containingMethodSymbol.Parameters.Length == 0 || + (containingMethodSymbol.Parameters.Length == 1 && containingMethodSymbol.Parameters[0] + .Type.Is("Unity.Entities.SystemState"))) + { + _systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression); + return ( + $"{systemStateExpression}.{CandidateSyntax.GetSimpleName(candidateSyntax.Node)}", + ReplacedWith.InvocationWithMissingArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + } + + SystemApiContextErrors.SGSA0002(_systemDescription, candidateSyntax); + break; + } + case CandidateType.BufferTypeHandle: + { + var @readonly = false; + var args = invocationExpressionSyntax.ArgumentList.Arguments.ToArray(); + if (args.Length == 0 || bool.TryParse(args[0].Expression.ToString(), out @readonly)) + { + var result = + _systemDescription.QueriesAndHandles.GetOrCreateTypeHandleField(typeArgument, + @readonly); + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetBufferTypeHandle(ref __TypeHandle.{result}, ref {systemStateExpression})", + ReplacedWith.InvocationWithFullArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + + var methodDeclarationSyntax = candidateSyntax.Node.AncestorOfKind(); + if (methodDeclarationSyntax.Identifier.ValueText == "OnCreate") + { + var containingMethodSymbol = semanticModel.GetDeclaredSymbol(methodDeclarationSyntax); + if (containingMethodSymbol.Parameters.Length == 0 || + (containingMethodSymbol.Parameters.Length == 1 && containingMethodSymbol.Parameters[0] + .Type.Is("Unity.Entities.SystemState"))) + { + _systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression); + return ( + $"{systemStateExpression}.{CandidateSyntax.GetSimpleName(candidateSyntax.Node)}", + ReplacedWith.InvocationWithMissingArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + } + + SystemApiContextErrors.SGSA0002(_systemDescription, candidateSyntax); + break; + } + case CandidateType.SharedComponentTypeHandle: + { + var @readonly = false; + var args = invocationExpressionSyntax.ArgumentList.Arguments.ToArray(); + if (args.Length == 0 || bool.TryParse(args[0].Expression.ToString(), out @readonly)) + { + var result = + _systemDescription.QueriesAndHandles.GetOrCreateTypeHandleField(typeArgument, + @readonly); + if (!_systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression)) + return default; + + return ( + $"global::Unity.Entities.Internal.InternalCompilerInterface.GetSharedComponentTypeHandle(ref __TypeHandle.{result}, ref {systemStateExpression})", + ReplacedWith.InvocationWithFullArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + + var methodDeclarationSyntax = candidateSyntax.Node.AncestorOfKind(); + if (methodDeclarationSyntax.Identifier.ValueText == "OnCreate") + { + var containingMethodSymbol = semanticModel.GetDeclaredSymbol(methodDeclarationSyntax); + if (containingMethodSymbol.Parameters.Length == 0 || + (containingMethodSymbol.Parameters.Length == 1 && containingMethodSymbol.Parameters[0] + .Type.Is("Unity.Entities.SystemState"))) + { + _systemDescription.TryGetSystemStateParameterName(candidateSyntax, + out var systemStateExpression); + return ( + $"{systemStateExpression}.{CandidateSyntax.GetSimpleName(candidateSyntax.Node)}", + ReplacedWith.InvocationWithMissingArgumentList, + ArgumentThatMightInvolveSystemApiInvocation1: default, + ArgumentThatMightInvolveSystemApiInvocation2: default); + } + } + + SystemApiContextErrors.SGSA0002(_systemDescription, candidateSyntax); + break; + } + } + + // If using a generic that takes part of a method, then it should default to a `InternalCompilerInterface.DontUseThisGetSingleQuery(this).` reference in SystemBase, and cause a compile error in ISystem. + bool TryGetSystemBaseGeneric(out string replacement, out ReplacedWith replacedWith) + { + replacement = invocationExpressionSyntax.Expression.ToString(); + replacedWith = ReplacedWith.InvocationWithMissingArgumentList; + + var usesUnknownTypeArgument = typeArgument is ITypeParameterSymbol; + + var usingUnkownTypeArgumentIsValid = false; + var containingTypeTypeList = + _systemDescription.SystemTypeSyntax + .TypeParameterList; // Can support parents but better restrictive now. + + if (containingTypeTypeList != null) + { + var validConstraints = containingTypeTypeList.Parameters; + foreach (var validConstraint in validConstraints) + usingUnkownTypeArgumentIsValid |= validConstraint.Identifier.ValueText == typeArgument.Name; + } + + if (usesUnknownTypeArgument && !usingUnkownTypeArgumentIsValid) + { + if (_systemDescription.SystemType == SystemType.ISystem) + SystemApiContextErrors.SGSA0001(_systemDescription, candidateSyntax); + + else if (isSystemApi && + candidateSyntax.Type == + CandidateType + .Singleton) // Enabled you to use type parameters for SystemAPI singletons inside SystemBase + { + var sn = CandidateSyntax.GetSimpleName(candidateSyntax.Node); + var noGenericGeneration = (candidateSyntax.Flags & CandidateFlags.NoGenericGeneration) == + CandidateFlags.NoGenericGeneration; + var memberAccessGeneric = + noGenericGeneration + ? sn.Identifier.ValueText + : sn.ToString(); // e.g. GetSingletonEntity -> query.GetSingletonEntity (with no generic) + + replacement = + $"global::Unity.Entities.Internal.InternalCompilerInterface.OnlyAllowedInSourceGeneratedCodeGetSingleQuery<{typeArgument.ToFullName()}>(this).{memberAccessGeneric}{invocationExpressionSyntax.ArgumentList.ToFullString()}"; + replacedWith = ReplacedWith.InvocationWithFullArgumentList; + } + + return true; + } + + return false; + } + + break; + } + } + + return default; + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemApiContextSyntaxWalker.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemApiContextSyntaxWalker.cs new file mode 100644 index 0000000..9215704 --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemApiContextSyntaxWalker.cs @@ -0,0 +1,184 @@ +using System; +using System.CodeDom.Compiler; +using System.Linq; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; + +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI; + +/* + The `SystemApiWalker` traverses through syntax nodes that have been marked by the `SystemApiContextModule` as candidates for patching. + Sometimes these candidate nodes might be nested within other candidate nodes -- e.g. in `SystemAPI.SetComponentEnabled(entity, !SystemAPI.IsComponentEnabled(entity));`, + both `SystemAPI.SetComponentEnabled(entity, !SystemAPI.GetComponentEnabled(entity))` and `SystemAPI.GetComponentEnabled(entity)` + are marked as candidates, with the latter nested inside the former. The `SystemApiWalker` will first patch the outer invocation by appending + + global::Unity.Entities.Internal.InternalCompilerInterface.SetComponentEnabledAfterCompletingDependency(ref cachedLookupHandle, ref systemState, + + before moving onwards to the `entity` argument originally passed to `SetComponentEnabled`. The walker will append the `entity` argument verbatim, + since the argument does not involve any `SystemAPI` invocation. The walker then continues traversing, and when it finally reaches the + `SystemAPI.IsComponentEnabled(entity)` node, it appends + + global::Unity.Entities.Internal.InternalCompilerInterface.IsComponentEnabledAfterCompletingDependency(ref cachedLookupHandle, ref systemState, + + before moving onwards to the `entity` argument originally passed to `IsComponentEnabled`. This argument will also be appended verbatim, since it + does not involve any `SystemAPI` invocation either. Finally, the walker appends all the missing closing parentheses to ensure that all the `InternalCompilerInterface` + invocations are correctly formed. The final code appended by the walker is thus: + + global::Unity.Entities.Internal.InternalCompilerInterface.SetComponentEnabledAfterCompletingDependency(ref cachedLookupHandle, ref systemState, entity, + global::Unity.Entities.Internal.InternalCompilerInterface.IsComponentEnabledAfterCompletingDependency(ref cachedLookupHandle, ref systemState, entity)); + */ +public partial class SystemApiContextSyntaxWalker : CSharpSyntaxWalker, IModuleSyntaxWalker +{ + private IndentedTextWriter _writer; + private readonly SystemDescription _systemDescription; + private bool _hasWrittenSyntax; + private int _numClosingBracketsForNestedSystemApiInvocations; + + public SystemApiContextSyntaxWalker(SystemDescription systemDescription) : base(SyntaxWalkerDepth.Trivia) => + _systemDescription = systemDescription; + + public bool TryWriteSyntax(IndentedTextWriter writer, CandidateSyntax candidateSyntax) + { + _writer = writer; + _hasWrittenSyntax = false; + _numClosingBracketsForNestedSystemApiInvocations = 0; + + // Begin depth-first traversal of the candidate node + Visit(candidateSyntax.Node); + + for (int i = 0; i < _numClosingBracketsForNestedSystemApiInvocations; i++) + _writer.Write(")"); + + // Cede write control back to the caller + return _hasWrittenSyntax; + } + + public override void VisitInvocationExpression(InvocationExpressionSyntax node) + { + // If the current node is a candidate for source generation + if (_systemDescription.CandidateNodes.TryGetValue(node, out CandidateSyntax candidateSyntax)) + { + var result = TryGetReplacementCode(node, candidateSyntax); + switch (result.ReplacedWith) + { + case ReplacedWith.InvocationWithMissingArgumentList: + _writer.Write(result.Replacement); + base.VisitArgumentList(node.ArgumentList); + _hasWrittenSyntax = true; + break; + case ReplacedWith.InvocationWithMissingSystemApiArguments: + _writer.Write(result.Replacement); + + // Visit the arguments of the current invocation to see whether they involve nested SystemAPI invocations. + // If yes, patch them accordingly. + if (result.ArgumentThatMightInvolveSystemApiInvocation1 != null) + VisitArgument(result.ArgumentThatMightInvolveSystemApiInvocation1); + + if (result.ArgumentThatMightInvolveSystemApiInvocation2 != null) + { + _writer.Write(", "); + VisitArgument(result.ArgumentThatMightInvolveSystemApiInvocation2); + } + _writer.Write(")"); + _numClosingBracketsForNestedSystemApiInvocations--; + _hasWrittenSyntax = true; + break; + case ReplacedWith.NotReplaced: + // The current node does not require source generation -- it's thus the caller's job to write the current node + _hasWrittenSyntax = false; + break; + default: + _writer.Write(result.Replacement); + _hasWrittenSyntax = true; + break; + } + } + else + base.VisitInvocationExpression(node); + } + + public override void VisitMemberAccessExpression(MemberAccessExpressionSyntax node) + { + // If the current node is a candidate for source generation + if (_systemDescription.CandidateNodes.TryGetValue(node, out CandidateSyntax candidateSyntax)) + { + var result = TryGetSystemApiTimeReplacementCode(candidateSyntax); + + if (string.IsNullOrEmpty(result)) + { + // The current node does not require source generation -- it's thus the caller's job to write the current node + _hasWrittenSyntax = false; + } + else + { + _hasWrittenSyntax = true; + _writer.Write(result); + } + } + else + base.VisitMemberAccessExpression(node); + } + + public override void VisitIdentifierName(IdentifierNameSyntax node) + { + // If the current node is a candidate for source generation + if (_systemDescription.CandidateNodes.TryGetValue(node, out CandidateSyntax candidateSyntax)) + { + var result = TryGetSystemApiTimeReplacementCode(candidateSyntax); + + if (string.IsNullOrEmpty(result)) + { + // The current node does not require source generation -- it's thus the caller's job to write the current node + _hasWrittenSyntax = false; + } + else + { + _hasWrittenSyntax = true; + _writer.Write(result); + } + } + else + base.VisitIdentifierName(node); + } + + public override void VisitToken(SyntaxToken token) + { + VisitLeadingTrivia(token); + _writer.Write(token.Text); + VisitTrailingTrivia(token); + } + + public override void VisitTrivia(SyntaxTrivia trivia) + { + var triviaKind = trivia.Kind(); + + if (triviaKind == SyntaxKind.EndOfLineTrivia) + _writer.WriteLine(); + + else if (triviaKind != SyntaxKind.DisabledTextTrivia && + triviaKind != SyntaxKind.PreprocessingMessageTrivia && + triviaKind != SyntaxKind.IfDirectiveTrivia && + triviaKind != SyntaxKind.ElifDirectiveTrivia && + triviaKind != SyntaxKind.ElseDirectiveTrivia && + triviaKind != SyntaxKind.EndIfDirectiveTrivia && + triviaKind != SyntaxKind.RegionDirectiveTrivia && + triviaKind != SyntaxKind.EndRegionDirectiveTrivia && + triviaKind != SyntaxKind.DefineDirectiveTrivia && + triviaKind != SyntaxKind.UndefDirectiveTrivia && + triviaKind != SyntaxKind.ErrorDirectiveTrivia && + triviaKind != SyntaxKind.WarningDirectiveTrivia && + triviaKind != SyntaxKind.PragmaWarningDirectiveTrivia && + triviaKind != SyntaxKind.PragmaChecksumDirectiveTrivia && + triviaKind != SyntaxKind.ReferenceDirectiveTrivia && + triviaKind != SyntaxKind.BadDirectiveTrivia && + triviaKind != SyntaxKind.SingleLineCommentTrivia && + triviaKind != SyntaxKind.MultiLineCommentTrivia) + { + if (!trivia.HasStructure) + _writer.Write(trivia.ToString()); + } + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemContextSystemModule.CandidateSyntax.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemContextSystemModule.CandidateSyntax.cs deleted file mode 100644 index 7ee41d2..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemContextSystemModule.CandidateSyntax.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.Common; - -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI -{ - public partial class SystemContextSystemModule - { - public readonly struct CandidateSyntax : ISystemCandidate { - public CandidateSyntax(CandidateType type, CandidateFlags flags, SyntaxNode node) { - Type = type; - Flags = flags; - Node = node; - } - - public string CandidateTypeName => $"SystemAPI.{Type.ToString()}"; - public SyntaxNode Node { get; } - public readonly CandidateType Type; - public readonly CandidateFlags Flags; - - public int GetOriginalLineNumber() => Node.GetLineNumber(); - - public static SyntaxNode GetFieldExpression(SyntaxNode newestNode) - => newestNode.Parent is MemberAccessExpressionSyntax { Expression: { } expression } - && (expression is IdentifierNameSyntax { Identifier: { ValueText: "SystemAPI" } } || expression is ThisExpressionSyntax) - ? newestNode.Parent : newestNode; - - public InvocationExpressionSyntax GetInvocationExpression(SyntaxNode newestNode) => newestNode as InvocationExpressionSyntax; - - public static SimpleNameSyntax GetSimpleName(SyntaxNode newestNode) { - if (!(newestNode is InvocationExpressionSyntax invocation)) return newestNode as SimpleNameSyntax; - return invocation.Expression switch { - MemberAccessExpressionSyntax member => member.Name, - SimpleNameSyntax sn => sn, - _ => null - }; - } - } - } - - public enum CandidateType { - TimeData, - GetComponentLookup, - GetComponent, - GetComponentRO, - GetComponentRW, - SetComponent, - HasComponent, - IsComponentEnabled, - SetComponentEnabled, - Singleton, - GetBufferLookup, - GetBuffer, - HasBuffer, - IsBufferEnabled, - SetBufferEnabled, - GetEntityStorageInfoLookup, - Exists, - Aspect, - TypeHandle, - } - - [Flags] - public enum CandidateFlags { - None = 0, - ReadOnly = 1, - NoGenericGeneration = 2, - All = int.MaxValue - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemContextSystemModule.SystemApiSystemReplacer.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemContextSystemModule.SystemApiSystemReplacer.cs deleted file mode 100644 index 370a738..0000000 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemContextSystemModule.SystemApiSystemReplacer.cs +++ /dev/null @@ -1,505 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -using Unity.Entities.SourceGen.Common; -using Unity.Entities.SourceGen.SystemGenerator.Common; - -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI -{ - public partial class SystemContextSystemModule - { - class SystemApiSystemReplacer : SystemRewriter where TAdditionalHandlesInfo : IAdditionalHandlesInfo, ISourceGeneratorDiagnosable - { - HandlesDescription m_HandlesDescription; - TAdditionalHandlesInfo m_AdditionalHandlesInfo; - - ReadOnlyCollection m_Candidates; - - public override IEnumerable NodesToTrack => m_Candidates.Select(c=>c.Node); - - StatementHashStack m_StatementHashStack; - public SystemApiSystemReplacer(ReadOnlyCollection candidates, HandlesDescription handlesDescription, TAdditionalHandlesInfo additionalHandlesInfo) { - m_Candidates = candidates; - m_HandlesDescription = handlesDescription; - m_AdditionalHandlesInfo = additionalHandlesInfo; - m_StatementHashStack = StatementHashStack.CreateInstance(); - } - - Dictionary m_SyntaxToCandidate; - public override SyntaxNode VisitTrackedSystem(SyntaxNode systemRootNode, string originalFilePath) - { - m_OriginalFilePath = originalFilePath; - - m_SyntaxToCandidate = new Dictionary(m_Candidates.Count); - foreach (var candidate in m_Candidates) - { - var newNode = systemRootNode.GetCurrentNodes(candidate.Node).FirstOrDefault() ?? candidate.Node; - m_SyntaxToCandidate.Add(newNode, candidate); - } - - return Visit(systemRootNode); - } - - // These need to be saved as Visiting can ascend from recursion until we find the node we actually want to replace - bool m_HasChangedMember; - SyntaxNode m_NodeToReplace; - SyntaxNode m_ReplacementNode; - int m_OriginalLineNumber; - - // Actual statement insertion and replacement occurs here - public override SyntaxNode Visit(SyntaxNode nodeIn) - { - if (nodeIn == null) - return null; - - // Visit children (and allow replacements to occur there) - var replacedNodeAndChildren = base.Visit(nodeIn); - - // Perform replacement of candidates - if (m_SyntaxToCandidate.TryGetValue(nodeIn, out var candidate)) { - var (original, replacement) = TryGetReplacementNodeFromCandidate(candidate, replacedNodeAndChildren, nodeIn); - if (replacement != null) - { - m_ReplacementNode = replacement; - m_NodeToReplace = original; - m_OriginalLineNumber = candidate.GetOriginalLineNumber(); - } - } - - if (replacedNodeAndChildren == m_NodeToReplace) { - replacedNodeAndChildren = m_ReplacementNode; - m_HasChangedMember = true; - } - - // Insert statement prolog before replacement - if (replacedNodeAndChildren is StatementSyntax statement && nodeIn == m_StatementHashStack.ActiveStatement) { - var poppedStatement = m_StatementHashStack.PopSyntax(); - poppedStatement.Add(statement.WithHiddenLineTrivia() as StatementSyntax); - poppedStatement[0] = poppedStatement[0].WithLineTrivia(m_OriginalFilePath, m_OriginalLineNumber) as StatementSyntax; - - replacedNodeAndChildren = SyntaxFactory.Block(new SyntaxList(), - SyntaxFactory.MissingToken(SyntaxKind.OpenBraceToken), - new SyntaxList(poppedStatement), - SyntaxFactory.MissingToken(SyntaxKind.CloseBraceToken)); - } - - if (replacedNodeAndChildren is MemberDeclarationSyntax memberSyntax && m_HasChangedMember) { - RecordChangedMember(memberSyntax); - m_HasChangedMember = false; - } - - return replacedNodeAndChildren; - } - - (SyntaxNode original, SyntaxNode replacement) TryGetReplacementNodeFromCandidate(CandidateSyntax candidate, SyntaxNode newestNode, SyntaxNode newestNonReplaced) { - var semanticModel = m_AdditionalHandlesInfo.SemanticModel; - - var resolveCandidateSymbol = semanticModel.GetSymbolInfo(candidate.Node); - var nodeSymbol = resolveCandidateSymbol.Symbol ?? resolveCandidateSymbol.CandidateSymbols.FirstOrDefault(); - var parentTypeInfo = nodeSymbol?.ContainingType; - - var fullName = parentTypeInfo?.ToFullName(); - var isSystemApi = fullName == "global::Unity.Entities.SystemAPI"; - var isManagedApi = fullName == "global::Unity.Entities.SystemAPI.ManagedAPI"; - if (!(isSystemApi || isManagedApi || (candidate.Type == CandidateType.Singleton && parentTypeInfo.Is("Unity.Entities.ComponentSystemBase")))) - return (null,null); - - switch (candidate.Type) { - case CandidateType.TimeData: { - return m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression) - ? (CandidateSyntax.GetFieldExpression(newestNode), SyntaxFactory.ParseExpression($"{systemStateExpression}.WorldUnmanaged.Time")) - : (null, null); - } - } - - // No type argument (EntityStorageInfoLookup, Exists) - if (nodeSymbol is IMethodSymbol { TypeArguments: { Length: 0 } }) { - var invocationExpression = candidate.GetInvocationExpression(newestNode); - switch (candidate.Type) { - case CandidateType.GetEntityStorageInfoLookup: - case CandidateType.Exists: - { - var storageInfoLookup = m_HandlesDescription.GetOrCreateEntityStorageInfoLookupField(); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{storageInfoLookup}.Update(ref {systemStateExpression});"); - - return candidate.Type switch - { - CandidateType.GetEntityStorageInfoLookup => (invocationExpression, ExpressionSyntaxWithTypeHandle(storageInfoLookup)), - CandidateType.Exists => (invocationExpression, InvocationExpression(ExpressionSyntaxWithTypeHandle(storageInfoLookup), "Exists", invocationExpression.ArgumentList)), - _ => throw new ArgumentOutOfRangeException() // Shouldn't be hit as outer switch is checking that only correct candidates go in! - }; - } - - case CandidateType.TypeHandle: - { - var typeHandleField = m_HandlesDescription.GetOrCreateEntityTypeHandleField(); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) return (null, null); - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{typeHandleField}.Update(ref {systemStateExpression});"); - return (invocationExpression, ExpressionSyntaxWithTypeHandle(typeHandleField)); - } - } - - } - - // Based on type argument - else if (nodeSymbol is IMethodSymbol { TypeArguments: { Length: 1 } } namedTypeSymbolWithTypeArg) { - var typeArgument = namedTypeSymbolWithTypeArg.TypeArguments.First(); - var invocationExpression = candidate.GetInvocationExpression(newestNode); - if (TryGetSystemBaseGeneric(out var replacer)) - return replacer; - - switch (candidate.Type) { - // Component - case CandidateType.GetComponentLookup: { - var @readonly = false; - var args = invocationExpression.ArgumentList.Arguments.ToArray(); - if (args.Length == 0 || bool.TryParse(args[0].Expression.ToString(), out @readonly)) { - var lookup = m_HandlesDescription.GetOrCreateComponentLookupField(typeArgument, @readonly); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{lookup}.Update(ref {systemStateExpression});"); - return (invocationExpression, ExpressionSyntaxWithTypeHandle(lookup)); - } - - var methodDeclarationSyntax = candidate.Node.AncestorOfKind(); - if (methodDeclarationSyntax.Identifier.ValueText == "OnCreate") { - var containingMethodSymbol = semanticModel.GetDeclaredSymbol(methodDeclarationSyntax); - if (containingMethodSymbol.Parameters.Length == 0 || - (containingMethodSymbol.Parameters.Length == 1 && containingMethodSymbol.Parameters[0].Type.Is("Unity.Entities.SystemState"))) - { - m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression); // Ok to not handle as you can't be in OnCreate without it. By definition of above SystemState constraint. - var sn = CandidateSyntax.GetSimpleName(newestNode); - return (invocationExpression, - SyntaxFactory.InvocationExpression(SyntaxFactory.ParseExpression($"{systemStateExpression}.{sn}"), invocationExpression.ArgumentList)); - } - } - - SystemAPIErrors.SGSA0002(m_AdditionalHandlesInfo, candidate); - - break; - } - case CandidateType.GetComponent when isManagedApi: { - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - return (invocationExpression, InvocationExpression($"{systemStateExpression}.EntityManager","GetComponentObject", - candidate.Node.DescendantNodes().OfType().First().TypeArgumentList, invocationExpression.ArgumentList)); - } - case CandidateType.GetComponent: { - var lookup = m_HandlesDescription.GetOrCreateComponentLookupField(typeArgument, true); - var entitySnippet = invocationExpression.ArgumentList.Arguments.First().Expression; - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{lookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement, $"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRO<{typeArgument.ToFullName()}>();"); - return (invocationExpression, ElementAccessExpression(ExpressionSyntaxWithTypeHandle(lookup), entitySnippet)); - } - case CandidateType.GetComponentRO: { - var lookup = m_HandlesDescription.GetOrCreateComponentLookupField(typeArgument, true); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{lookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement, $"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRO<{typeArgument.ToFullName()}>();"); - return (invocationExpression, InvocationExpression(ExpressionSyntaxWithTypeHandle(lookup), "GetRefRO", invocationExpression.ArgumentList)); - } - case CandidateType.GetComponentRW: { - var lookup = m_HandlesDescription.GetOrCreateComponentLookupField(typeArgument, false); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{lookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement, $"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRW<{typeArgument.ToFullName()}>();"); - return (invocationExpression, InvocationExpression(ExpressionSyntaxWithTypeHandle(lookup), "GetRefRW", invocationExpression.ArgumentList)); - } - case CandidateType.SetComponent: { - var args = invocationExpression.ArgumentList.Arguments.ToArray(); - if (args.Length != 2) return (null, null); - var (entityArg, componentArg) = args[0].NameColon?.Name.Identifier.ValueText == "component" ? (args[1], args[0]) : (args[0], args[1]); - typeArgument = typeArgument.TypeKind == TypeKind.TypeParameter - ? semanticModel.GetTypeInfo(componentArg.Expression).Type - : typeArgument; - - var lookup = m_HandlesDescription.GetOrCreateComponentLookupField(typeArgument, false); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{lookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement, $"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRW<{typeArgument.ToFullName()}>();"); - return (invocationExpression, SyntaxFactory.AssignmentExpression(SyntaxKind.SimpleAssignmentExpression, ElementAccessExpression(ExpressionSyntaxWithTypeHandle(lookup), entityArg.Expression), componentArg.Expression)); - } - case CandidateType.HasComponent when isManagedApi: { - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - return (invocationExpression, InvocationExpression($"{systemStateExpression}.EntityManager","HasComponent", - candidate.Node.DescendantNodes().OfType().First().TypeArgumentList, invocationExpression.ArgumentList)); - } - case CandidateType.HasComponent: { - var lookup = m_HandlesDescription.GetOrCreateComponentLookupField(typeArgument, true); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{lookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement, $"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRO<{typeArgument.ToFullName()}>();"); - return (invocationExpression, InvocationExpression(ExpressionSyntaxWithTypeHandle(lookup), "HasComponent", invocationExpression.ArgumentList)); - } - case CandidateType.IsComponentEnabled when isManagedApi: { - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) return (null, null); - return (invocationExpression, InvocationExpression($"{systemStateExpression}.EntityManager","IsComponentEnabled", - candidate.Node.DescendantNodes().OfType().First().TypeArgumentList, invocationExpression.ArgumentList)); - } - case CandidateType.IsComponentEnabled: { - var lookup = m_HandlesDescription.GetOrCreateComponentLookupField(typeArgument, true); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{lookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement, $"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRO<{typeArgument.ToFullName()}>();"); - return (invocationExpression, InvocationExpression(ExpressionSyntaxWithTypeHandle(lookup), "IsComponentEnabled", invocationExpression.ArgumentList)); - } - case CandidateType.SetComponentEnabled when isManagedApi: { - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) return (null, null); - return (invocationExpression, InvocationExpression($"{systemStateExpression}.EntityManager","SetComponentEnabled", - candidate.Node.DescendantNodes().OfType().First().TypeArgumentList, invocationExpression.ArgumentList)); - } - case CandidateType.SetComponentEnabled: { - var lookup = m_HandlesDescription.GetOrCreateComponentLookupField(typeArgument, false); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{lookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement, $"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRW<{typeArgument.ToFullName()}>();"); - return (invocationExpression, InvocationExpression(ExpressionSyntaxWithTypeHandle(lookup), "SetComponentEnabled", invocationExpression.ArgumentList)); - } - - // Buffer - case CandidateType.GetBufferLookup: { - var @readonly = false; - var args = invocationExpression.ArgumentList.Arguments.ToArray(); - if (args.Length == 0 || bool.TryParse(args[0].Expression.ToString(), out @readonly)) { - var bufferLookup = m_HandlesDescription.GetOrCreateBufferLookupField(typeArgument, @readonly); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{bufferLookup}.Update(ref {systemStateExpression});"); - return (invocationExpression, ExpressionSyntaxWithTypeHandle(bufferLookup)); - } - - var methodDeclarationSyntax = candidate.Node.AncestorOfKind(); - if (methodDeclarationSyntax.Identifier.ValueText == "OnCreate") { - var containingMethodSymbol = semanticModel.GetDeclaredSymbol(methodDeclarationSyntax); - if (containingMethodSymbol.Parameters.Length == 0 || - (containingMethodSymbol.Parameters.Length == 1 && containingMethodSymbol.Parameters[0].Type.Is("Unity.Entities.SystemState"))) - { - m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression); - var sn = CandidateSyntax.GetSimpleName(newestNode); - return (invocationExpression, SyntaxFactory.InvocationExpression(SyntaxFactory.ParseExpression($"{systemStateExpression}.{sn}"), - invocationExpression.ArgumentList)); - } - } - - SystemAPIErrors.SGSA0002(m_AdditionalHandlesInfo, candidate); - - break; - } - case CandidateType.GetBuffer: { - var bufferLookup = m_HandlesDescription.GetOrCreateBufferLookupField(typeArgument, false); - var entitySnippet = invocationExpression.ArgumentList.Arguments.First().Expression; - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{bufferLookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement,$"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRW<{typeArgument.ToFullName()}>();"); // Todo in next PR, change SysApi signature to match GetBuffer of EntityManager - return (invocationExpression, ElementAccessExpression(ExpressionSyntaxWithTypeHandle(bufferLookup), entitySnippet)); - } - case CandidateType.HasBuffer: { - var bufferLookup = m_HandlesDescription.GetOrCreateBufferLookupField(typeArgument, true); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{bufferLookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement,$"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRO<{typeArgument.ToFullName()}>();"); - return (invocationExpression, InvocationExpression(ExpressionSyntaxWithTypeHandle(bufferLookup), "HasBuffer", invocationExpression.ArgumentList)); - } - case CandidateType.IsBufferEnabled: { - var bufferLookup = m_HandlesDescription.GetOrCreateBufferLookupField(typeArgument, true); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{bufferLookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement,$"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRO<{typeArgument.ToFullName()}>();"); - return (invocationExpression, InvocationExpression(ExpressionSyntaxWithTypeHandle(bufferLookup), "IsBufferEnabled", invocationExpression.ArgumentList)); - } - case CandidateType.SetBufferEnabled: { - var bufferLookup = m_HandlesDescription.GetOrCreateBufferLookupField(typeArgument, false); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{bufferLookup}.Update(ref {systemStateExpression});"); - m_StatementHashStack.PushStatement(statement,$"{systemStateExpression}.EntityManager.CompleteDependencyBeforeRW<{typeArgument.ToFullName()}>();"); - return (invocationExpression, InvocationExpression(ExpressionSyntaxWithTypeHandle(bufferLookup), "SetBufferEnabled", invocationExpression.ArgumentList)); - } - - // Singleton - case CandidateType.Singleton: { - var queryFieldName = m_HandlesDescription.GetOrCreateQueryField( - new SingleArchetypeQueryFieldDescription( - new Archetype( - new[] - { - new Query - { - IsReadOnly = (candidate.Flags & CandidateFlags.ReadOnly) == CandidateFlags.ReadOnly, - Type = QueryType.All, - TypeSymbol = typeArgument - } - }, - Array.Empty(), - Array.Empty(), - Array.Empty(), - Array.Empty(), - EntityQueryOptions.Default | EntityQueryOptions.IncludeSystems) - )); - - var sn = CandidateSyntax.GetSimpleName(newestNode); - var noGenericGeneration = (candidate.Flags & CandidateFlags.NoGenericGeneration) == CandidateFlags.NoGenericGeneration; - var memberAccess = noGenericGeneration ? sn.Identifier.ValueText : sn.ToString(); // e.g. GetSingletonEntity -> query.GetSingletonEntity (with no generic) - - return (invocationExpression, InvocationExpression($"{queryFieldName}", memberAccess, invocationExpression.ArgumentList)); - } - - // Aspect - case CandidateType.Aspect: { - var @readonly = (candidate.Flags == CandidateFlags.ReadOnly); - - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) - return (null, null); - - var entitySnippet = invocationExpression.ArgumentList.Arguments.First(); - var aspectLookup = m_HandlesDescription.GetOrCreateAspectLookup(typeArgument, @readonly); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{aspectLookup}.Update(ref {systemStateExpression});"); - var completeDependencyStatement = @readonly switch - { - true => $"{typeArgument.ToFullName()}.CompleteDependencyBeforeRO(ref {systemStateExpression});", - false => $"{typeArgument.ToFullName()}.CompleteDependencyBeforeRW(ref {systemStateExpression});" - }; - m_StatementHashStack.PushStatement(statement, completeDependencyStatement); - - var replacementElementAccessExpression = SyntaxFactory.ElementAccessExpression(ExpressionSyntaxWithTypeHandle(aspectLookup)) - .WithArgumentList(SyntaxFactory.BracketedArgumentList(SyntaxFactory.SingletonSeparatedList(entitySnippet))); - - return (invocationExpression, replacementElementAccessExpression); - } - - // TypeHandle - case CandidateType.TypeHandle: { - var @readonly = false; - var args = invocationExpression.ArgumentList.Arguments.ToArray(); - if (args.Length == 0 || bool.TryParse(args[0].Expression.ToString(), out @readonly)) - { - var typeHandleField = m_HandlesDescription.GetOrCreateTypeHandleField(typeArgument, @readonly); - if (!m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression)) return (null, null); - - var statement = newestNonReplaced.AncestorOfKind(); - m_StatementHashStack.PushStatement(statement, $"__TypeHandle.{typeHandleField}.Update(ref {systemStateExpression});"); - return (invocationExpression, ExpressionSyntaxWithTypeHandle(typeHandleField)); - } - - var methodDeclarationSyntax = candidate.Node.AncestorOfKind(); - if (methodDeclarationSyntax.Identifier.ValueText == "OnCreate") { - var containingMethodSymbol = semanticModel.GetDeclaredSymbol(methodDeclarationSyntax); - if (containingMethodSymbol.Parameters.Length == 0 || - (containingMethodSymbol.Parameters.Length == 1 && containingMethodSymbol.Parameters[0].Type.Is("Unity.Entities.SystemState"))) - { - m_AdditionalHandlesInfo.TryGetSystemStateParameterName(candidate, out var systemStateExpression); - var sn = CandidateSyntax.GetSimpleName(newestNode); - return (invocationExpression, SyntaxFactory.InvocationExpression(SyntaxFactory.ParseExpression($"{systemStateExpression}.{sn}"), - invocationExpression.ArgumentList)); - } - } - - SystemAPIErrors.SGSA0002(m_AdditionalHandlesInfo, candidate); - - break; - } - } - - // If using a generic that takes part of a method, then it should default to a `InternalCompilerInterface.DontUseThisGetSingleQuery(this).` reference in SystemBase, and cause a compile error in ISystem. - bool TryGetSystemBaseGeneric(out (SyntaxNode original, SyntaxNode replacement) replacer) - { - replacer = (null, null); - - var usesUnknownTypeArgument = typeArgument is ITypeParameterSymbol; - - var usingUnkownTypeArgumentIsValid = false; - var containingTypeTypeList = m_AdditionalHandlesInfo.TypeSyntax.TypeParameterList; // Can support parents but better restrictive now. - if (containingTypeTypeList != null) - { - var validConstraints = containingTypeTypeList.Parameters; - foreach (var validConstraint in validConstraints) - usingUnkownTypeArgumentIsValid |= validConstraint.Identifier.ValueText == typeArgument.Name; - } - - if (usesUnknownTypeArgument && !usingUnkownTypeArgumentIsValid) { - if (m_AdditionalHandlesInfo.SystemType==SystemType.ISystem) - SystemAPIErrors.SGSA0001(m_AdditionalHandlesInfo, candidate); - else if (isSystemApi && candidate.Type == CandidateType.Singleton) // Enabled you to use type parameters for SystemAPI singletons inside SystemBase - { - var sn = CandidateSyntax.GetSimpleName(newestNode); - var noGenericGenerationGeneric = (candidate.Flags & CandidateFlags.NoGenericGeneration) == CandidateFlags.NoGenericGeneration; - var memberAccessGeneric = noGenericGenerationGeneric ? sn.Identifier.ValueText : sn.ToString(); // e.g. GetSingletonEntity -> query.GetSingletonEntity (with no generic) - - var dontUseThisGetSingleQueryInvocation = InvocationExpression( - "Unity.Entities.Internal.InternalCompilerInterface", - "OnlyAllowedInSourceGeneratedCodeGetSingleQuery", - TypeArgumentListSyntax(typeArgument.ToFullName()), - ArgumentListSyntax(SyntaxFactory.ThisExpression())); - replacer = (invocationExpression, InvocationExpression(dontUseThisGetSingleQueryInvocation, memberAccessGeneric, invocationExpression.ArgumentList)); - } - return true; - } - - return false; - } - } - - return (null, null); - } - - static ArgumentListSyntax ArgumentListSyntax(ExpressionSyntax expression) - => SyntaxFactory.ArgumentList(SyntaxFactory.SeparatedList(new[] { SyntaxFactory.Argument(expression) })); - static TypeArgumentListSyntax TypeArgumentListSyntax(string typeName) - => SyntaxFactory.TypeArgumentList(SyntaxFactory.SeparatedList(new[] { (TypeSyntax)SyntaxFactory.IdentifierName(typeName)})); - static InvocationExpressionSyntax InvocationExpression(ExpressionSyntax from, string invocation, ArgumentListSyntax args) - => SyntaxFactory.InvocationExpression(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, from, SyntaxFactory.IdentifierName(invocation)), args); - static InvocationExpressionSyntax InvocationExpression(string from, string invocation, TypeArgumentListSyntax typeArgs, ArgumentListSyntax args) - => SyntaxFactory.InvocationExpression(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(@from), SyntaxFactory.GenericName(SyntaxFactory.Identifier(invocation), typeArgs)), args); - static InvocationExpressionSyntax InvocationExpression(string from, string invocation, ArgumentListSyntax args) - => SyntaxFactory.InvocationExpression(SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName(@from), SyntaxFactory.IdentifierName(invocation)), args); - static ElementAccessExpressionSyntax ElementAccessExpression(ExpressionSyntax componentLookup, ExpressionSyntax entitySnippet) - => SyntaxFactory.ElementAccessExpression(componentLookup, SyntaxFactory.BracketedArgumentList(SyntaxFactory.SeparatedList(new []{SyntaxFactory.Argument(entitySnippet)}))); - static ExpressionSyntax ExpressionSyntaxWithTypeHandle(string fieldName) - => SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression, SyntaxFactory.IdentifierName("__TypeHandle"), SyntaxFactory.IdentifierName(fieldName)); - } - } -} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemContextSystemModule.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemContextSystemModule.cs index d364fc0..d9b97aa 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemContextSystemModule.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/SystemContextSystemModule.cs @@ -4,162 +4,192 @@ using Unity.Entities.SourceGen.Common; using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI +namespace Unity.Entities.SourceGen.SystemGenerator.SystemAPI; + +public class SystemContextSystemModule : ISystemModule { - public partial class SystemContextSystemModule : ISystemModule { - readonly Dictionary> m_Candidates = new Dictionary>(); - public IEnumerable<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)> Candidates { - get { - foreach (var type in m_Candidates) { - foreach (var candidate in type.Value) { - yield return (candidate.Node, type.Key); - } + readonly Dictionary> m_Candidates = new Dictionary>(); + + public IEnumerable<(SyntaxNode SyntaxNode, TypeDeclarationSyntax SystemType)> Candidates + { + get { + foreach (var type in m_Candidates) { + foreach (var candidate in type.Value) { + yield return (candidate.Node, type.Key); } } } - public bool RequiresReferenceToBurst => false; + } + + public bool RequiresReferenceToBurst => false; + + public void OnReceiveSyntaxNode(SyntaxNode node, Dictionary candidateOwnership) + { + switch (node) + { + case InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax { Name: { } nameSyntax } } invocation: // makes sure to use SystemAPI.*** as the reference instead of *** as the SystemAPI part should also dissapear + InvocationWithNameOrMember(invocation, nameSyntax); + break; + case InvocationExpressionSyntax { Expression: SimpleNameSyntax nameSyntax } invocation: + InvocationWithNameOrMember(invocation, nameSyntax); + break; + case IdentifierNameSyntax nameSyntax: + PropertyWithNameOrMember(nameSyntax); + break; + } - public void OnReceiveSyntaxNode(SyntaxNode node) { - switch (node) { - case InvocationExpressionSyntax { Expression: MemberAccessExpressionSyntax { Name: { } nameSyntax } } invocation: // makes sure to use SystemAPI.*** as the reference instead of *** as the SystemAPI part should also dissapear - InvocationWithNameOrMember(invocation, nameSyntax); + void PropertyWithNameOrMember(SimpleNameSyntax nameSyntax) + { + switch (nameSyntax.Identifier.ValueText) + { + case "Time": + var systemTypeSyntax = nameSyntax.AncestorOfKindOrDefault(); + if (systemTypeSyntax != null) + { + SyntaxNode candidateNode; + if (nameSyntax.Parent is MemberAccessExpressionSyntax memberAccessExpressionSyntax) + { + // E.g. In the case of "Time.DeltaTime", "Time" is the expression and what we want to replace + if (memberAccessExpressionSyntax.Expression == nameSyntax) + candidateNode = nameSyntax; + else + // E.g. In the case of "SystemAPI.Time", we want to replace the entire expression + candidateNode = memberAccessExpressionSyntax; + } + else + // E.g. In the case of "Time" (where SystemAPI is not explicitly written), we want to replace the entire expression + candidateNode = nameSyntax; + + var candidateSyntax = new CandidateSyntax(CandidateType.TimeData, CandidateFlags.None, candidateNode); + candidateOwnership[candidateNode] = candidateSyntax; + m_Candidates.Add(systemTypeSyntax, candidateSyntax); + } break; - case InvocationExpressionSyntax { Expression: SimpleNameSyntax nameSyntax } invocation: - InvocationWithNameOrMember(invocation, nameSyntax); + } + } + + void InvocationWithNameOrMember(InvocationExpressionSyntax invocation, SimpleNameSyntax nodeContainedByInvocation) { + switch (nodeContainedByInvocation.Identifier.ValueText) { + // Component + case "GetComponentLookup": + AddCandidate(CandidateFlags.None, CandidateType.GetComponentLookup); break; - case IdentifierNameSyntax nameSyntax: - PropertyWithNameOrMember(nameSyntax); + case "GetComponent": + AddCandidate(CandidateFlags.None, CandidateType.GetComponent); + break; + case "GetComponentRO": + AddCandidate(CandidateFlags.None, CandidateType.GetComponentRO); + break; + case "GetComponentRW": + AddCandidate(CandidateFlags.None, CandidateType.GetComponentRW); + break; + case "SetComponent": + AddCandidate(CandidateFlags.None, CandidateType.SetComponent); + break; + case "HasComponent": + AddCandidate(CandidateFlags.None, CandidateType.HasComponent); + break; + case "IsComponentEnabled": + AddCandidate(CandidateFlags.None, CandidateType.IsComponentEnabled); + break; + case "SetComponentEnabled": + AddCandidate(CandidateFlags.None, CandidateType.SetComponentEnabled); break; - } - void PropertyWithNameOrMember(SimpleNameSyntax nameSyntax) { - switch (nameSyntax.Identifier.ValueText) { - case "Time": - var systemTypeSyntax = nameSyntax.AncestorOfKindOrDefault(); - if (systemTypeSyntax != null) - m_Candidates.Add(systemTypeSyntax, new CandidateSyntax(CandidateType.TimeData, CandidateFlags.None, nameSyntax)); - break; - } - } + // Buffer + case "GetBufferLookup": + AddCandidate(CandidateFlags.None, CandidateType.GetBufferLookup); + break; + case "GetBuffer": + AddCandidate(CandidateFlags.None, CandidateType.GetBuffer); + break; + case "HasBuffer": + AddCandidate(CandidateFlags.None, CandidateType.HasBuffer); + break; + case "IsBufferEnabled": + AddCandidate(CandidateFlags.None, CandidateType.IsBufferEnabled); + break; + case "SetBufferEnabled": + AddCandidate(CandidateFlags.None, CandidateType.SetBufferEnabled); + break; - void InvocationWithNameOrMember(InvocationExpressionSyntax invocation, SimpleNameSyntax nodeContainedByInvocation) { - switch (nodeContainedByInvocation.Identifier.ValueText) { - // Component - case "GetComponentLookup": - AddCandidate(CandidateFlags.None, CandidateType.GetComponentLookup); - break; - case "GetComponent": - AddCandidate(CandidateFlags.None, CandidateType.GetComponent); - break; - case "GetComponentRO": - AddCandidate(CandidateFlags.None, CandidateType.GetComponentRO); - break; - case "GetComponentRW": - AddCandidate(CandidateFlags.None, CandidateType.GetComponentRW); - break; - case "SetComponent": - AddCandidate(CandidateFlags.None, CandidateType.SetComponent); - break; - case "HasComponent": - AddCandidate(CandidateFlags.None, CandidateType.HasComponent); - break; - case "IsComponentEnabled": - AddCandidate(CandidateFlags.None, CandidateType.IsComponentEnabled); - break; - case "SetComponentEnabled": - AddCandidate(CandidateFlags.None, CandidateType.SetComponentEnabled); - break; - - // Buffer - case "GetBufferLookup": - AddCandidate(CandidateFlags.None, CandidateType.GetBufferLookup); - break; - case "GetBuffer": - AddCandidate(CandidateFlags.None, CandidateType.GetBuffer); - break; - case "HasBuffer": - AddCandidate(CandidateFlags.None, CandidateType.HasBuffer); - break; - case "IsBufferEnabled": - AddCandidate(CandidateFlags.None, CandidateType.IsBufferEnabled); - break; - case "SetBufferEnabled": - AddCandidate(CandidateFlags.None, CandidateType.SetBufferEnabled); - break; - - // StorageInfo/Exists - case "GetEntityStorageInfoLookup": - AddCandidate(CandidateFlags.None, CandidateType.GetEntityStorageInfoLookup); - break; - case "Exists": - AddCandidate(CandidateFlags.None, CandidateType.Exists); - break; - - // Singleton - case "GetSingleton": - AddCandidate(CandidateFlags.ReadOnly, CandidateType.Singleton); - break; - case "GetSingletonEntity": - AddCandidate(CandidateFlags.ReadOnly | CandidateFlags.NoGenericGeneration, CandidateType.Singleton); - break; - case "SetSingleton": - AddCandidate(CandidateFlags.None, CandidateType.Singleton); - break; - case "GetSingletonRW": - AddCandidate(CandidateFlags.None, CandidateType.Singleton); - break; - case "TryGetSingletonRW": - AddCandidate(CandidateFlags.None, CandidateType.Singleton); - break; - case "TryGetSingletonBuffer": - AddCandidate(CandidateFlags.None, CandidateType.Singleton); - break; - case "TryGetSingletonEntity": - AddCandidate(CandidateFlags.ReadOnly, CandidateType.Singleton); - break; - case "GetSingletonBuffer": - AddCandidate(CandidateFlags.None, CandidateType.Singleton); - break; - case "TryGetSingleton": - AddCandidate(CandidateFlags.ReadOnly, CandidateType.Singleton); - break; - case "HasSingleton": - AddCandidate(CandidateFlags.ReadOnly, CandidateType.Singleton); - break; - - // Aspect - case "GetAspect": - AddCandidate(CandidateFlags.None, CandidateType.Aspect); - break; - - // TypeHandle - case "GetEntityTypeHandle": - AddCandidate(CandidateFlags.None, CandidateType.TypeHandle); - break; - case "GetComponentTypeHandle": - AddCandidate(CandidateFlags.None, CandidateType.TypeHandle); - break; - case "GetBufferTypeHandle": - AddCandidate(CandidateFlags.None, CandidateType.TypeHandle); - break; - case "GetSharedComponentTypeHandle": - AddCandidate(CandidateFlags.None, CandidateType.TypeHandle); - break; + // StorageInfo/Exists + case "GetEntityStorageInfoLookup": + AddCandidate(CandidateFlags.None, CandidateType.GetEntityStorageInfoLookup); + break; + case "Exists": + AddCandidate(CandidateFlags.None, CandidateType.Exists); + break; + + // Singleton + case "GetSingleton": + AddCandidate(CandidateFlags.ReadOnly, CandidateType.Singleton); + break; + case "GetSingletonEntity": + AddCandidate(CandidateFlags.ReadOnly | CandidateFlags.NoGenericGeneration, CandidateType.Singleton); + break; + case "SetSingleton": + AddCandidate(CandidateFlags.None, CandidateType.Singleton); + break; + case "GetSingletonRW": + AddCandidate(CandidateFlags.None, CandidateType.Singleton); + break; + case "TryGetSingletonRW": + AddCandidate(CandidateFlags.None, CandidateType.Singleton); + break; + case "TryGetSingletonBuffer": + AddCandidate(CandidateFlags.None, CandidateType.Singleton); + break; + case "TryGetSingletonEntity": + AddCandidate(CandidateFlags.ReadOnly, CandidateType.Singleton); + break; + case "GetSingletonBuffer": + AddCandidate(CandidateFlags.None, CandidateType.Singleton); + break; + case "TryGetSingleton": + AddCandidate(CandidateFlags.ReadOnly, CandidateType.Singleton); + break; + case "HasSingleton": + AddCandidate(CandidateFlags.ReadOnly, CandidateType.Singleton); + break; + + // Aspect + case "GetAspect": + AddCandidate(CandidateFlags.None, CandidateType.Aspect); + break; + + // TypeHandle + case "GetEntityTypeHandle": + AddCandidate(CandidateFlags.None, CandidateType.EntityTypeHandle); + break; + case "GetComponentTypeHandle": + AddCandidate(CandidateFlags.None, CandidateType.ComponentTypeHandle); + break; + case "GetBufferTypeHandle": + AddCandidate(CandidateFlags.None, CandidateType.BufferTypeHandle); + break; + case "GetSharedComponentTypeHandle": + AddCandidate(CandidateFlags.None, CandidateType.SharedComponentTypeHandle); + break; void AddCandidate(CandidateFlags flags, CandidateType type) { + var candidateSyntax = new CandidateSyntax(type, flags, invocation); + candidateOwnership[invocation] = candidateSyntax; + m_Candidates.Add( nodeContainedByInvocation.AncestorOfKind(), - new CandidateSyntax(type, flags, invocation)); + candidateSyntax); } - } } } + } - public bool RegisterChangesInSystem(SystemDescription desc) { - if (!m_Candidates.ContainsKey(desc.SystemTypeSyntax)) return false; - var candidatesInSystem = m_Candidates[desc.SystemTypeSyntax]; - desc.Rewriters.Add(new SystemApiSystemReplacer(candidatesInSystem.AsReadOnly(), desc.HandlesDescription, desc)); - return true; - } + public bool RegisterChangesInSystem(SystemDescription desc) + { + if (!m_Candidates.ContainsKey(desc.SystemTypeSyntax)) + return false; + + desc.SyntaxWalkers.Add(Module.SystemApiContext, new SystemApiContextSyntaxWalker(desc)); + return true; } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.csproj b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.csproj index cf93c60..bd6c129 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.csproj +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator.SystemAPI/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.csproj @@ -3,7 +3,9 @@ true netstandard2.0 - 8.0 + latest + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator/PartialStructWarningSuppressor.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator/PartialStructWarningSuppressor.cs index 8bdccd6..addd395 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator/PartialStructWarningSuppressor.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator/PartialStructWarningSuppressor.cs @@ -11,29 +11,30 @@ namespace Unity.Entities.SourceGen.SystemGenerator [DiagnosticAnalyzer(LanguageNames.CSharp)] public class PartialStructWarningSuppressor : DiagnosticSuppressor { - static readonly SuppressionDescriptor _partialStructWarningRule = new SuppressionDescriptor("SPDC0282", "CS0282", + static readonly SuppressionDescriptor PartialStructWarningRule = new SuppressionDescriptor("SPDC0282", "CS0282", "Some DOTS types utilize codegen requiring the type to be partial."); static readonly string[] _allowedPartialStructInterfaces = { "ISystem", "ISystemBase", "IJobEntity", "IAspect" }; - public override ImmutableArray SupportedSuppressions => ImmutableArray.Create(_partialStructWarningRule); + public override ImmutableArray SupportedSuppressions => ImmutableArray.Create(PartialStructWarningRule); public override void ReportSuppressions(SuppressionAnalysisContext context) { - foreach (var diagnostic in context.ReportedDiagnostics.Where(diagnostic => diagnostic.Id == _partialStructWarningRule.SuppressedDiagnosticId)) + foreach (var diagnostic in context.ReportedDiagnostics.Where(diagnostic => diagnostic.Id == PartialStructWarningRule.SuppressedDiagnosticId)) { context.CancellationToken.ThrowIfCancellationRequested(); - var node = diagnostic.Location.SourceTree.GetRoot(context.CancellationToken).FindNode(diagnostic.Location.SourceSpan); + var node = diagnostic.Location.SourceTree?.GetRoot(context.CancellationToken).FindNode(diagnostic.Location.SourceSpan); if (node is StructDeclarationSyntax {BaseList: { } baseList} structSyntax) if (baseList.Types.Any(type => _allowedPartialStructInterfaces.Contains(type.ToString()))) - context.ReportSuppression(Suppression.Create(_partialStructWarningRule, diagnostic)); + context.ReportSuppression(Suppression.Create(PartialStructWarningRule, diagnostic)); else { var semanticModel = context.GetSemanticModel(structSyntax.SyntaxTree); var typeSymbol = semanticModel.GetDeclaredSymbol(structSyntax); + if (_allowedPartialStructInterfaces.Select(@interface => $"Unity.Entities.{@interface}").Any(@interface => typeSymbol.InheritsFromInterface(@interface))) - context.ReportSuppression(Suppression.Create(_partialStructWarningRule, diagnostic)); + context.ReportSuppression(Suppression.Create(PartialStructWarningRule, diagnostic)); } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemGenerator.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemGenerator.cs index 1eb6d23..9f25587 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemGenerator.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemGenerator.cs @@ -7,160 +7,183 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.Common; using Unity.Entities.SourceGen.SystemGenerator.Common; -using Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations; -namespace Unity.Entities.SourceGen.SystemGenerator +namespace Unity.Entities.SourceGen.SystemGenerator; + +[Generator] +public class SystemGenerator : ISourceGenerator { - [Generator] - public class SystemGenerator : ISourceGenerator - { - const string GeneratorName = "System"; - public void Initialize(GeneratorInitializationContext context) => - context.RegisterForSyntaxNotifications(() => new SystemSyntaxReceiver(context.CancellationToken)); + const string GeneratorName = "System"; - public void Execute(GeneratorExecutionContext context) - { - if (!SourceGenHelpers.IsBuildTime || !SourceGenHelpers.ShouldRun(context.Compilation, context.CancellationToken)) - return; + public void Initialize(GeneratorInitializationContext context) => + context.RegisterForSyntaxNotifications(() => new SystemSyntaxReceiver(context.CancellationToken)); - SourceOutputHelpers.Setup(context.ParseOptions, context.AdditionalFiles); + public void Execute(GeneratorExecutionContext context) + { + if (!SourceGenHelpers.IsBuildTime || !SourceGenHelpers.ShouldRun(context.Compilation, context.CancellationToken)) + return; + + SourceOutputHelpers.Setup(context.ParseOptions, context.AdditionalFiles); - Location lastLocation = null; - SourceOutputHelpers.LogInfoToSourceGenLog($"Source generating assembly {context.Compilation.Assembly.Name}..."); + Location lastLocation = null; + SourceOutputHelpers.LogInfoToSourceGenLog($"Source generating assembly {context.Compilation.Assembly.Name}..."); - var stopwatch = Stopwatch.StartNew(); + var stopwatch = Stopwatch.StartNew(); - try + try + { + var systemReceiver = (SystemSyntaxReceiver)context.SyntaxReceiver; + var allModules = systemReceiver.SystemModules; + var syntaxTreesWithCandidate = SystemGeneratorHelper.GetSyntaxTreesWithCandidates(allModules); + var assemblyHasReferenceToBurst = context.Compilation.ReferencedAssemblyNames.Any(n => n.Name == "Unity.Burst"); + var assemblyHasReferenceToCollections = context.Compilation.ReferencedAssemblyNames.Any(n => n.Name == "Unity.Collections"); + var requiresMissingReferenceToBurst = false; + var preprocessorInfo = PreprocessorInfo.From(context.ParseOptions.PreprocessorSymbolNames); + + // If multiple user-written parts exist for a given partial type, then each part might have its own syntax tree and semantic model, and thus its own `SystemDescription`. + // Therefore we cannot group `SystemDescription`s by syntax tree or by semantic model, because we might end up wrongly treating partial parts of the same type + // as distinct types. Instead, we group `SystemDescription`s by the fully qualified names of the system types they handle. + var systemTypeFullNamesToDescriptions = new Dictionary>(); + + // Process all candidates and create `SystemDescription`s for viable candidates + // Store results in `systemTypeFullNamesToDescriptions`. + foreach (var syntaxTree in syntaxTreesWithCandidate) { - var systemReceiver = (SystemSyntaxReceiver)context.SyntaxReceiver; - var allModules = systemReceiver.SystemModules; - var syntaxTreesWithCandidate = SystemGeneratorHelper.GetSyntaxTreesWithCandidates(allModules); - var assemblyHasReferenceToBurst = context.Compilation.ReferencedAssemblyNames.Any(n => n.Name == "Unity.Burst"); - var assemblyHasReferenceToCollections = context.Compilation.ReferencedAssemblyNames.Any(n => n.Name == "Unity.Collections"); - var requiresMissingReferenceToBurst = false; - - // If multiple user-written parts exist for a given partial type, then each part might have its own syntax tree and semantic model, and thus its own `SystemDescription`. - // Therefore we cannot group `SystemDescription`s by syntax tree or by semantic model, because we might end up wrongly treating partial parts of the same type - // as distinct types. Instead, we group `SystemDescription`s by the fully qualified names of the system types they handle. - var systemTypeFullNamesToDescriptions = new Dictionary>(); - - // Process all candidates and create `SystemDescription`s for viable candidates - // Store results in `systemTypeFullNamesToDescriptions`. - foreach (var syntaxTree in syntaxTreesWithCandidate) + context.CancellationToken.ThrowIfCancellationRequested(); + + var syntaxTreeInfo = new SyntaxTreeInfo { Tree = syntaxTree, IsSourceGenerationSuccessful = true }; + var semanticModel = context.Compilation.GetSemanticModel(syntaxTree); + + foreach (var systemTypeSyntax in SystemGeneratorHelper.GetSystemTypesInTree(syntaxTree, allModules)) { context.CancellationToken.ThrowIfCancellationRequested(); + if (systemTypeSyntax.Identifier.ValueText == "SystemBase") + continue; - var syntaxTreeInfo = new SyntaxTreeInfo { Tree = syntaxTree, IsSourceGenerationSuccessful = true }; - var semanticModel = context.Compilation.GetSemanticModel(syntaxTree); + lastLocation = systemTypeSyntax.GetLocation(); - foreach (var systemTypeSyntax in SystemGeneratorHelper.GetSystemTypesInTree(syntaxTree, allModules)) - { - context.CancellationToken.ThrowIfCancellationRequested(); - if (systemTypeSyntax.Identifier.ValueText == "SystemBase") - continue; + if (systemReceiver.ISystemDefinedAsClass.Contains(systemTypeSyntax)) + continue; - lastLocation = systemTypeSyntax.GetLocation(); + var systemTypeSymbol = semanticModel.GetDeclaredSymbol(systemTypeSyntax); + var systemTypeInfo = systemTypeSymbol.TryGetSystemType(); + if (!systemTypeInfo.IsSystemType) + continue; - if (systemReceiver.ISystemDefinedAsClass.Contains(systemTypeSyntax)) - continue; + SystemDescription systemDescription; + if (systemReceiver.CandidateNodesGroupedBySystemType.TryGetValue(systemTypeSyntax, out var candidates)) + { + systemDescription = + new SystemDescription( + systemTypeSyntax, + systemTypeInfo.SystemType, + systemTypeSymbol, + semanticModel, + syntaxTreeInfo, + candidates, + preprocessorInfo); + } + else + { + systemDescription = + new SystemDescription( + systemTypeSyntax, + systemTypeInfo.SystemType, + systemTypeSymbol, + semanticModel, + syntaxTreeInfo, + default, + preprocessorInfo); + } - var systemTypeSymbol = semanticModel.GetDeclaredSymbol(systemTypeSyntax); - var systemTypeInfo = systemTypeSymbol.TryGetSystemType(); - if (!systemTypeInfo.IsSystemType) - continue; + foreach (var module in SystemGeneratorHelper.GetAllModulesWithCandidatesInSystemType(systemTypeSyntax, allModules, context)) + { + context.CancellationToken.ThrowIfCancellationRequested(); - var systemDescription = new SystemDescription(systemTypeSyntax, systemTypeInfo.SystemType, systemTypeSymbol, semanticModel, context.ParseOptions.PreprocessorSymbolNames, syntaxTreeInfo); + if (!module.RegisterChangesInSystem(systemDescription)) + continue; - foreach (var module in SystemGeneratorHelper.GetAllModulesWithCandidatesInSystemType(systemTypeSyntax, allModules, context)) + if (module.RequiresReferenceToBurst && !assemblyHasReferenceToBurst) { - context.CancellationToken.ThrowIfCancellationRequested(); - - if (!module.RegisterChangesInSystem(systemDescription)) - continue; - - if (module.RequiresReferenceToBurst && !assemblyHasReferenceToBurst) - { - requiresMissingReferenceToBurst = true; - syntaxTreeInfo.IsSourceGenerationSuccessful = false; - } + requiresMissingReferenceToBurst = true; + syntaxTreeInfo.IsSourceGenerationSuccessful = false; } + } - foreach (var systemDescriptionDiagnostic in systemDescription.Diagnostics) - context.ReportDiagnostic(systemDescriptionDiagnostic); - systemDescription.Diagnostics.Clear(); + foreach (var systemDescriptionDiagnostic in systemDescription.SourceGenDiagnostics) + context.ReportDiagnostic(systemDescriptionDiagnostic); - if (!systemDescription.ContainsChangesToSystem()) - continue; + systemDescription.SourceGenDiagnostics.Clear(); - systemTypeFullNamesToDescriptions.Add(systemDescription.SystemTypeFullName, systemDescription); - } + if (!systemDescription.ContainsChangesToSystem()) + continue; + + systemTypeFullNamesToDescriptions.Add(systemDescription.SystemTypeFullName, systemDescription); } + } - var partialSystemTypesGroupedBySyntaxTrees = new Dictionary>(); + var partialSystemTypesGroupedBySyntaxTrees = new Dictionary>(); - // Generate partial types and store results in `partialSystemTypesGroupedBySyntaxTrees` - foreach (var kvp in systemTypeFullNamesToDescriptions) - { - var allDescriptionsForSameSystemType = kvp.Value; - var generatedPart = PartialSystemTypeGenerator.Generate(allDescriptionsForSameSystemType.ToArray()); - foreach (var systemDescriptionDiagnostic in allDescriptionsForSameSystemType.SelectMany(d=>d.Diagnostics)) - context.ReportDiagnostic(systemDescriptionDiagnostic); + // Generate partial types and store results in `partialSystemTypesGroupedBySyntaxTrees` + foreach (var kvp in systemTypeFullNamesToDescriptions) + { + var allDescriptionsForSameSystemType = kvp.Value; + var generatedPart = PartialSystemTypeGenerator.Generate(allDescriptionsForSameSystemType.ToArray()); - if (partialSystemTypesGroupedBySyntaxTrees.TryGetValue(generatedPart.SyntaxTreeInfo, - out var foundDict)) { - foundDict[generatedPart.OriginalSystem] = generatedPart.GeneratedPartialSystem; - } else { - partialSystemTypesGroupedBySyntaxTrees.Add(generatedPart.SyntaxTreeInfo, - new Dictionary - { { generatedPart.OriginalSystem, generatedPart.GeneratedPartialSystem } }); - } - } + foreach (var desc in allDescriptionsForSameSystemType) + foreach (var diag in desc.SourceGenDiagnostics) + context.ReportDiagnostic(diag); - // Generate source files in parallel for debugging purposes (very useful to be able to visually inspect generated code!). - // Add generated source to compilation only if there are no errors. - foreach (var kvp in partialSystemTypesGroupedBySyntaxTrees) + if (partialSystemTypesGroupedBySyntaxTrees.TryGetValue(generatedPart.SyntaxTreeInfo, out var partialSystemTypes)) + partialSystemTypes[generatedPart.OriginalSystem] = generatedPart.GeneratedSyntaxTreeContainingGeneratedPartialSystem; + else { - var syntaxTreeInfo = kvp.Key; - var replacements = kvp.Value; + partialSystemTypesGroupedBySyntaxTrees.Add( + generatedPart.SyntaxTreeInfo, new Dictionary + { { generatedPart.OriginalSystem, generatedPart.GeneratedSyntaxTreeContainingGeneratedPartialSystem } }); + } + } - var rootNodes = TypeCreationHelpers.GetReplacedRootNodes(syntaxTreeInfo.Tree, replacements); - if (rootNodes.Count == 0) - return; + // Generate source files in parallel for debugging purposes (very useful to be able to visually inspect generated code!). + // Add generated source to compilation only if there are no errors. + foreach (var kvp in partialSystemTypesGroupedBySyntaxTrees) + { + var syntaxTreeInfo = kvp.Key; + var originalSystemToGeneratedPartialSystem = kvp.Value; - context.CancellationToken.ThrowIfCancellationRequested(); - var outputSource = TypeCreationHelpers.GenerateSourceTextForRootNodes(GeneratorName, context, syntaxTreeInfo.Tree, rootNodes); + for (int i = 0; i < originalSystemToGeneratedPartialSystem.Count; i++) + { + var originalToGeneratedPartial = originalSystemToGeneratedPartialSystem.ElementAt(i); + var generatedFile = syntaxTreeInfo.Tree.GetGeneratedSourceFilePath(context.Compilation.Assembly.Name, GeneratorName, salting: i); + var outputSource = TypeCreationHelpers.FixUpLineDirectivesAndOutputSource(generatedFile.FullFilePath, originalToGeneratedPartial.Value); - // Only output source to compilation if we are successful (failing early in this case will speed up compilation and avoid non-useful errors) if (syntaxTreeInfo.IsSourceGenerationSuccessful) - context.AddSource(syntaxTreeInfo.Tree.GetGeneratedSourceFileName(GeneratorName), outputSource); + context.AddSource(generatedFile.FileNameOnly, outputSource); SourceOutputHelpers.OutputSourceToFile( - syntaxTreeInfo.Tree.GetGeneratedSourceFilePath(context.Compilation.Assembly.Name, GeneratorName), + generatedFile.FullFilePath, () => outputSource.ToString()); } + } - foreach (var iSystemDefinedAsClass in systemReceiver.ISystemDefinedAsClass) - { - SystemGeneratorErrors.DC0065(context, iSystemDefinedAsClass.GetLocation(), - iSystemDefinedAsClass.Identifier.ValueText); - } + foreach (var iSystemDefinedAsClass in systemReceiver.ISystemDefinedAsClass) + SystemGeneratorErrors.DC0065(context, iSystemDefinedAsClass.GetLocation(), iSystemDefinedAsClass.Identifier.ValueText); - if (requiresMissingReferenceToBurst) - SystemGeneratorErrors.DC0060(context, context.Compilation.SyntaxTrees.First().GetRoot().GetLocation(), context.Compilation.AssemblyName); - else if (!assemblyHasReferenceToCollections) - SystemGeneratorErrors.DC0061(context, context.Compilation.SyntaxTrees.First().GetRoot().GetLocation(), context.Compilation.AssemblyName); + if (requiresMissingReferenceToBurst) + SystemGeneratorErrors.DC0060(context, context.Compilation.SyntaxTrees.First().GetRoot().GetLocation(), context.Compilation.AssemblyName); + else if (!assemblyHasReferenceToCollections) + SystemGeneratorErrors.DC0061(context, context.Compilation.SyntaxTrees.First().GetRoot().GetLocation(), context.Compilation.AssemblyName); - stopwatch.Stop(); - SourceOutputHelpers.LogInfoToSourceGenLog( - $"TIME : SystemGenerator : {context.Compilation.Assembly.Name} : {stopwatch.ElapsedMilliseconds}ms"); - } - catch (Exception exception) - { - if (exception is OperationCanceledException) - throw; + stopwatch.Stop(); - context.LogError("SGICE002", "SystemGenerator", exception.ToUnityPrintableString(), lastLocation ?? context.Compilation.SyntaxTrees.First().GetRoot().GetLocation()); - } + SourceOutputHelpers.LogInfoToSourceGenLog($"TIME : SystemGenerator : {context.Compilation.Assembly.Name} : {stopwatch.ElapsedMilliseconds}ms"); + } + catch (Exception exception) + { + if (exception is OperationCanceledException) + throw; + + context.LogError("SGICE002", "SystemGenerator", exception.ToUnityPrintableString(), lastLocation ?? context.Compilation.SyntaxTrees.First().GetRoot().GetLocation()); } } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemGeneratorHelper.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemGeneratorHelper.cs index 28dc121..9f7116a 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemGeneratorHelper.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemGeneratorHelper.cs @@ -4,30 +4,31 @@ using Microsoft.CodeAnalysis.CSharp.Syntax; using Unity.Entities.SourceGen.SystemGenerator.Common; -namespace Unity.Entities.SourceGen.SystemGenerator +namespace Unity.Entities.SourceGen.SystemGenerator; + +public static class SystemGeneratorHelper { - public static class SystemGeneratorHelper + public static IEnumerable GetSyntaxTreesWithCandidates(IEnumerable allModules) + => allModules.SelectMany(module => module.Candidates).Select(node => node.SyntaxNode.SyntaxTree).Distinct(); + + public static HashSet GetSystemTypesInTree(SyntaxTree syntaxTree, IEnumerable allModules) { - public static IEnumerable GetSyntaxTreesWithCandidates(IEnumerable allModules) - => allModules.SelectMany(module => module.Candidates).Select(node => node.SyntaxNode.SyntaxTree).Distinct(); + var uniqueSystems = new HashSet(); - public static IEnumerable GetSystemTypesInTree(SyntaxTree syntaxTree, IEnumerable allModules) - { - return - allModules - .SelectMany(m => m.Candidates) - .Where(c => c.SyntaxNode.SyntaxTree == syntaxTree) - .Select(c => c.SystemType) - .Distinct(); - } + foreach (var module in allModules) + foreach (var candidate in module.Candidates) + if (candidate.SyntaxNode.SyntaxTree == syntaxTree) + uniqueSystems.Add(candidate.SystemType); - public static IEnumerable GetAllModulesWithCandidatesInSystemType( - TypeDeclarationSyntax systemTypeWithCandidate, IEnumerable allModules, GeneratorExecutionContext generatorExecutionContext) - { - return - allModules - .Where(m => m.Candidates.Any(c => c.SystemType == systemTypeWithCandidate)) - .Distinct(); - } + return uniqueSystems; + } + + public static IEnumerable GetAllModulesWithCandidatesInSystemType( + TypeDeclarationSyntax systemTypeWithCandidate, IEnumerable allModules, GeneratorExecutionContext generatorExecutionContext) + { + return + allModules + .Where(m => m.Candidates.Any(c => c.SystemType == systemTypeWithCandidate)) + .Distinct(); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemSyntaxReceiver.cs b/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemSyntaxReceiver.cs index a6d52c3..06d530b 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemSyntaxReceiver.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator/SystemSyntaxReceiver.cs @@ -1,93 +1,104 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; +using System.Collections.Immutable; using System.Diagnostics; using System.Linq; using System.Threading; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; +using Unity.Entities.SourceGen.Common; using Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; -using Unity.Entities.SourceGen.JobEntity; -using Unity.Entities.SourceGen.LambdaJobs; using Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder; using Unity.Entities.SourceGen.SystemGenerator.SystemAPI; using Unity.Entities.SourceGen.SystemGenerator.Common; +using Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations; +using Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; +using JobEntityModule = Unity.Entities.SourceGen.JobEntityGenerator.JobEntityModule; -namespace Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations +namespace Unity.Entities.SourceGen.SystemGenerator; + +public class SystemSyntaxReceiver : ISyntaxReceiver { - public class SystemSyntaxReceiver : ISyntaxReceiver - { - internal HashSet ISystemDefinedAsClass = new HashSet(); - readonly CancellationToken _cancelationToken; + internal readonly HashSet ISystemDefinedAsClass = new(); + + readonly Dictionary _markedNodes = new(); + readonly CancellationToken _cancellationToken; + + internal Dictionary> CandidateNodesGroupedBySystemType + => _markedNodes + .GroupBy(n => n.Key.AncestorOfKind()) + .ToDictionary( + group => group.Key, + group => group.ToDictionary(kvp => kvp.Key, kvp => kvp.Value)); - internal IReadOnlyCollection SystemModules { get; } + internal IReadOnlyCollection SystemModules { get; } - public SystemSyntaxReceiver(CancellationToken cancellationToken) + public SystemSyntaxReceiver(CancellationToken cancellationToken) + { + _cancellationToken = cancellationToken; + SystemModules = new ISystemModule[] { - _cancelationToken = cancellationToken; - SystemModules = new ISystemModule[] - { - new LambdaJobsModule(), - new JobEntityModule(), - new EntityQueryModule(), - new IdiomaticCSharpForEachModule(), - new SystemContextSystemModule(), - new SystemAPIQueryBuilderModule() - }; - } + new LambdaJobsModule(), + new JobEntityModule(), + new EntityQueryModule(), + new IfeModule(), + new SystemContextSystemModule(), + new SystemApiQueryBuilderModule() + }; + } - public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + public void OnVisitSyntaxNode(SyntaxNode syntaxNode) + { + if (IsValidSystemType(syntaxNode)) { - if (IsValidSystemType(syntaxNode)) + foreach (var module in SystemModules) { - foreach (var module in SystemModules) - { - _cancelationToken.ThrowIfCancellationRequested(); - module.OnReceiveSyntaxNode(syntaxNode); - } + _cancellationToken.ThrowIfCancellationRequested(); + module.OnReceiveSyntaxNode(syntaxNode, _markedNodes); } - - _cancelationToken.ThrowIfCancellationRequested(); } - bool IsValidSystemType(SyntaxNode syntaxNode) - { - if (syntaxNode is TypeDeclarationSyntax typeSyntax) - { - if (typeSyntax.BaseList == null) - return false; - - var hasPartial = false; - foreach (var modifier in typeSyntax.Modifiers) - if (modifier.IsKind(SyntaxKind.PartialKeyword)) - { - hasPartial = true; - break; - } + _cancellationToken.ThrowIfCancellationRequested(); + } - if (!hasPartial) - return false; + bool IsValidSystemType(SyntaxNode syntaxNode) + { + if (syntaxNode is TypeDeclarationSyntax typeSyntax) + { + if (typeSyntax.BaseList == null) + return false; - // error if class ISystem - // this can also be an analyzer with codefix in future - if (syntaxNode is ClassDeclarationSyntax && BaseListContains(typeSyntax, "ISystem")) + var hasPartial = false; + foreach (var modifier in typeSyntax.Modifiers) + if (modifier.IsKind(SyntaxKind.PartialKeyword)) { - ISystemDefinedAsClass.Add(typeSyntax); - return false; + hasPartial = true; + break; } - } - return true; - } + if (!hasPartial) + return false; - static bool BaseListContains(TypeDeclarationSyntax typeSyntax, string typeOrInterfaceName) - { - Debug.Assert(typeSyntax.BaseList != null, "typeSyntax.BaseList != null"); - foreach (var t in typeSyntax.BaseList.Types) + // error if class ISystem + // this can also be an analyzer with codefix in future + if (syntaxNode is ClassDeclarationSyntax && BaseListContains(typeSyntax, "ISystem")) { - if (t.Type.ToString().Split('.').Last() == typeOrInterfaceName) - return true; + ISystemDefinedAsClass.Add(typeSyntax); + return false; } - return false; } + return true; + } + + static bool BaseListContains(TypeDeclarationSyntax typeSyntax, string typeOrInterfaceName) + { + Debug.Assert(typeSyntax.BaseList != null, "typeSyntax.BaseList != null"); + foreach (var t in typeSyntax.BaseList.Types) + { + if (t.Type.ToString().Split('.').Last() == typeOrInterfaceName) + return true; + } + return false; } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGenerator/Unity.Entities.SourceGen.SystemGenerator.csproj b/Unity.Entities/SourceGenerators/Source~/SystemGenerator/Unity.Entities.SourceGen.SystemGenerator.csproj index bd111f4..4b6b207 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGenerator/Unity.Entities.SourceGen.SystemGenerator.csproj +++ b/Unity.Entities/SourceGenerators/Source~/SystemGenerator/Unity.Entities.SourceGen.SystemGenerator.csproj @@ -3,7 +3,9 @@ true netstandard2.0 - 8.0 + latest + True + AnyCPU diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/AspectGenerator/AspectErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/AspectGenerator/AspectErrorTests.cs index 2c8396b..5732148 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/AspectGenerator/AspectErrorTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/AspectGenerator/AspectErrorTests.cs @@ -50,6 +50,8 @@ public async Task SGA0002_ImplementedIAspectCreateOfDifferentType() public readonly RefRW Data; public MyStruct CreateAspect(Entity entity, ref SystemState system) => default; public void AddComponentRequirementsTo(ref UnsafeList all){} + public void CompleteDependencyBeforeRO(ref SystemState state){} + public void CompleteDependencyBeforeRW(ref SystemState state){} }|}"; var expected = VerifyCS.CompilerError(nameof(AspectErrors.SGA0002)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/EntityQuery/EntityQueryErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/EntityQuery/EntityQueryErrorTests.cs index 3472c40..2e93bd9 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/EntityQuery/EntityQueryErrorTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/EntityQuery/EntityQueryErrorTests.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Threading.Tasks; using Unity.Entities.SourceGen.SystemGenerator; +using Unity.Entities.SourceGen.SystemGenerator.Common; using VerifyCS = Unity.Entities.SourceGenerators.Test.CSharpSourceGeneratorVerifier< Unity.Entities.SourceGen.SystemGenerator.SystemGenerator>; diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityErrorTests.cs index e605879..7697b3f 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityErrorTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityErrorTests.cs @@ -1,9 +1,10 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Threading.Tasks; +using Unity.Entities.SourceGen.JobEntityGenerator; using Unity.Entities.SourceGen.SystemGenerator.Common; using VerifyCS = Unity.Entities.SourceGenerators.Test.CSharpIncrementalGeneratorVerifier< - Unity.Entities.SourceGen.JobEntity.JobEntityGenerator>; + Unity.Entities.SourceGen.JobEntityGenerator.JobEntityGenerator>; namespace Unity.Entities.SourceGenerators; @@ -384,7 +385,7 @@ void Execute(in TData data) { } var expected = VerifyCS.CompilerError(nameof(JobEntityGeneratorErrors.SGJE0020)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } - + [TestMethod] public async Task SGJE0021_AspectByIn() { diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityNoErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityNoErrorTests.cs index 812a936..9e040e6 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityNoErrorTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityNoErrorTests.cs @@ -2,7 +2,7 @@ using System.Threading.Tasks; using VerifyCS = Unity.Entities.SourceGenerators.Test.CSharpIncrementalGeneratorVerifier< - Unity.Entities.SourceGen.JobEntity.JobEntityGenerator>; + Unity.Entities.SourceGen.JobEntityGenerator.JobEntityGenerator>; namespace Unity.Entities.SourceGenerators; diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityVerify.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityVerify.cs index 7109f66..b695792 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityVerify.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IJobEntity/JobEntityVerify.cs @@ -1,8 +1,8 @@ -using System.Threading.Tasks; +using System.Threading.Tasks; using Microsoft.VisualStudio.TestTools.UnitTesting; using VerifyCS = Unity.Entities.SourceGenerators.Test.CSharpIncrementalGeneratorVerifier< - Unity.Entities.SourceGen.JobEntity.JobEntityGenerator>; + Unity.Entities.SourceGen.JobEntityGenerator.JobEntityGenerator>; namespace Unity.Entities.SourceGenerators; @@ -77,10 +77,8 @@ public async Task JobEntityRemembersEarlyNestedEscape() const string source = @" using Unity.Entities; #if !UNITY_EDITOR - #if !UNITY_DOTSRUNTIME class MyClass {} #endif - #endif public partial struct MyJob : IJobEntity { @@ -100,9 +98,7 @@ partial class MyClass { #endregion #region ManagedComponents #if UNITY_DISABLE_MANAGED_COMPONENTS - #if !UNITY_DOTSRUNTIME void MyFunc1(){} - #endif void MyFunc2(){} #endif public partial struct MyJob : IJobEntity diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/ILPP/PatchableMethodsNoErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/ILPP/PatchableMethodsNoErrorTests.cs new file mode 100644 index 0000000..ac33f9d --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/ILPP/PatchableMethodsNoErrorTests.cs @@ -0,0 +1,133 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Threading.Tasks; +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp; +using Microsoft.CodeAnalysis.Testing; +using Mono.Cecil; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; +using VerifyCS = + Unity.Entities.SourceGenerators.Test.CSharpSourceGeneratorVerifier< + Unity.Entities.SourceGen.SystemGenerator.SystemGenerator>; + +namespace Unity.Entities.SourceGenerators +{ + [TestClass] + public class PatchableMethodsNoErrorTests + { + [DataTestMethod] + // Check basic types + [DataRow("void Main(int test)")] + // Check nullables + [DataRow("void Main(int? test)")] + // Check modifiers + [DataRow("void Main(in int test)")] + [DataRow("void Main(ref int test)")] + // Check Arrays + [DataRow("void Main(int[] boi)")] + [DataRow("void Main(int[,] boi)")] + [DataRow("void Main(int[,,] boi)")] + [DataRow("void Main(int[,,,,,,,,,,,,,,,] boi)")] + // Check Tuples + [DataRow("void Main((int,float) value)")] + [DataRow("void Main((int,float, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int, int) value)")] + // Check Generics + [DataRow("void Main(T1 val)")] + [DataRow("void Main(System.Collections.Generic.List.IDK>> test1)", "public struct TestType { public class IDK {}}")] + [DataRow("void Main(MyDicKey.MyDicValue>.Dictionary funky)", "public struct MyDicKey { public struct MyDicValue { public struct Dictionary {} }}")] + // Check Function Pointers + [DataRow("unsafe void Main(delegate* funky)")] + [DataRow("unsafe void Main(delegate* unmanaged[Stdcall] funky)")] + [DataRow("unsafe void Main(delegate* unmanaged[Fastcall] funky)")] + [DataRow("unsafe void Main(delegate* unmanaged[Thiscall] funky)")] + [DataRow("unsafe void Main(delegate* unmanaged[Cdecl] funky)")] + [DataRow("unsafe void Main(delegate* unmanaged funky)")] + // Check properties + [DataRow("int Main", "", "get => 5;")] + // Check ovveride + [DataRow("protected override void Main(in int val)", "public abstract partial class Base { protected abstract void Main(in int message); }", "", "namespace MyNameSpace.Wonky { class Test : Base { ")] + [DataRow("protected override void Main(ref int val)", "public abstract partial class Base { protected abstract void Main(ref int message); }", "", "namespace MyNameSpace.Wonky { class Test : Base { ")] + [DataRow("protected override void Main(out int val)", "public abstract partial class Base { protected abstract void Main(out int message); }", "val = 5;", "namespace MyNameSpace.Wonky { class Test : Base { ")] + public async Task GeneratedRoslynSignatureMatchesCecil(string methodSignature, string additionalTypes = "", string returnStatement = "", string beforeSignature = "namespace MyNameSpace.Wonky { class Test { ") + { + // Setup C# project with a test.cs containing a method of the given signature + var workspace = new AdhocWorkspace(); + var project = workspace.AddProject("test", LanguageNames.CSharp); + project = project.WithCompilationOptions(new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, allowUnsafe: true)); + var refer = new ReferenceAssemblies("net6.0", new PackageIdentity("Microsoft.NETCore.App.Ref", "6.0.0"), Path.Combine("ref", "net6.0")); + project = project.WithMetadataReferences(refer.ResolveAsync(LanguageNames.CSharp, CancellationToken.None).Result); + project = project.AddDocument("test.cs", $"{beforeSignature} {methodSignature}{{{returnStatement}}} }} {additionalTypes} }}").Project; + + // Compile project into an assembly stream + var compilation = await project.GetCompilationAsync().ConfigureAwait(false); + var assemblyStream = new MemoryStream(); + var result = compilation!.Emit(assemblyStream); + Assert.IsTrue(result.Success, "Compilation failed because: " + string.Join(", ", result.Diagnostics.Select(d => d.GetMessage()))); + + // Read Assembly with Cecil + assemblyStream.Position = 0; + var myAssembly = AssemblyDefinition.ReadAssembly(assemblyStream); + var module = myAssembly.MainModule; + + // Find cecil method containing Main and generate ilName + MethodReference? mainMethod = null; + foreach (var method in module.GetType("MyNameSpace.Wonky.Test").Methods) + { + mainMethod = method; + if (mainMethod.Name.Contains("Main")) + break; + } + Assert.IsNotNull(mainMethod); + var ilName = GetMethodNameAndParamsAsString(mainMethod); + + // Find roslyn method containing Main and generate roslynName + var roslynSymbol = compilation.GetTypeByMetadataName("MyNameSpace.Wonky.Test")!.GetMembers().First(m => m.Name.Contains("Main")); + var roslynMethod = roslynSymbol switch + { + IMethodSymbol m => m, + IPropertySymbol p => p.GetMethod, + _ => null + }; + var roslynName = roslynMethod?.GetMethodAndParamsAsString(default(DiagnosticBag)); + + // Assert that Cecil and Roslyn Generated Cecil matches + Assert.AreEqual(ilName, roslynName, $"GeneratedCecil: {roslynName}, ActualCecil: {ilName}"); + await assemblyStream.DisposeAsync(); + } + + struct DiagnosticBag : ISourceGeneratorDiagnosable + { + public List SourceGenDiagnostics { get; } + } + + // Copied from `Cloner.cs` please keep in sync + static string GetMethodNameAndParamsAsString(MethodReference method) + { + var strBuilder = new StringBuilder(); + strBuilder.Append(method.Name); + strBuilder.Append($"_T{method.GenericParameters.Count}"); + + foreach (var parameter in method.Parameters) + { + if (parameter.ParameterType.IsByReference) + { + if (parameter.IsIn) + strBuilder.Append($"_in"); + else if (parameter.IsOut) + strBuilder.Append($"_out"); + else + strBuilder.Append($"_ref"); + } + + strBuilder.Append($"_{parameter.ParameterType}"); + } + + return strBuilder.ToString(); + } + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IdiomaticCSharpForEach/ForEachErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IdiomaticCSharpForEach/ForEachErrorTests.cs index a68334b..6fddd0a 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IdiomaticCSharpForEach/ForEachErrorTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IdiomaticCSharpForEach/ForEachErrorTests.cs @@ -3,6 +3,7 @@ using Unity.Entities.SourceGen.Common; using Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query; using Unity.Entities.SourceGen.SystemGenerator; +using Unity.Entities.SourceGen.SystemGenerator.Common; using VerifyCS = Unity.Entities.SourceGenerators.Test.CSharpSourceGeneratorVerifier< Unity.Entities.SourceGen.SystemGenerator.SystemGenerator>; @@ -18,13 +19,13 @@ public async Task SGFE002_ForEachIterationThroughAspectQuery_InPropertyAccessor( const string source = @" using Unity.Entities; using Unity.Entities.Tests; - {|#0:partial struct TranslationSystem : ISystem + partial struct TranslationSystem : ISystem { public int Translation { get { - foreach (var translation in SystemAPI.Query>()){} + foreach (var translation in {|#0:SystemAPI.Query>()|}){} return 3; } } @@ -34,8 +35,8 @@ public void OnCreate(ref SystemState state) { } public void OnDestroy(ref SystemState state) { } public void OnUpdate(ref SystemState state) { } - }|}"; - var expected = VerifyCS.CompilerError(nameof(IdiomaticCSharpForEachCompilerMessages.SGFE002)).WithLocation(0); + }"; + var expected = VerifyCS.CompilerError(nameof(IfeCompilerMessages.SGFE002)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } @@ -220,7 +221,7 @@ public void OnUpdate(ref SystemState state) } public void OnDestroy(ref SystemState state) { } }"; - var expected = VerifyCS.CompilerError(nameof(IdiomaticCSharpForEachCompilerMessages.SGFE003)).WithLocation(0); + var expected = VerifyCS.CompilerError(nameof(IfeCompilerMessages.SGFE003)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } @@ -236,12 +237,12 @@ public void OnCreate(ref SystemState state) { } public void OnUpdate(ref SystemState state) { foreach (var component in {|#0:SystemAPI.Query>() - .WithSharedComponentFilter() - .WithSharedComponentFilter()|}) {} + .WithSharedComponentFilter(new EcsTestSharedCompA(), new EcsTestSharedCompB()) + .WithSharedComponentFilter(new EcsTestSharedComp(inValue: 5))|}) {} } public void OnDestroy(ref SystemState state) { } }"; - var expected = VerifyCS.CompilerError(nameof(IdiomaticCSharpForEachCompilerMessages.SGFE007)).WithLocation(0); + var expected = VerifyCS.CompilerError(nameof(IfeCompilerMessages.SGFE007)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } @@ -264,7 +265,7 @@ public void OnUpdate(ref SystemState state) public void OnDestroy(ref SystemState state) { } }"; - var expected = VerifyCS.CompilerError(nameof(IdiomaticCSharpForEachCompilerMessages.SGFE008)).WithLocation(0); + var expected = VerifyCS.CompilerError(nameof(IfeCompilerMessages.SGFE008)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } @@ -281,7 +282,7 @@ protected override void OnUpdate() { } public QueryEnumerable> GetEntitiesWithEcsTestData() => {|#0:SystemAPI.Query>()|}; }"; - var expected = VerifyCS.CompilerError(nameof(IdiomaticCSharpForEachCompilerMessages.SGFE001)).WithLocation(0); + var expected = VerifyCS.CompilerError(nameof(IfeCompilerMessages.SGFE001)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } @@ -304,52 +305,109 @@ public void OnUpdate(ref SystemState state) public void OnDestroy(ref SystemState state) { } }"; - var expected = VerifyCS.CompilerInfo(nameof(IdiomaticCSharpForEachCompilerMessages.SGFE009)).WithLocation(0); + var expected = VerifyCS.CompilerInfo(nameof(IfeCompilerMessages.SGFE009)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } [TestMethod] - public async Task SGFE010_ValueTypeComponentDataInForEachIteration() + public async Task ForEachIteration_WithDataWithCycle() { const string source = @" using Unity.Entities; using Unity.Entities.Tests; - partial struct CharacterMovementSystem : ISystem + public struct CycleData : IComponentData { - public void OnCreate(ref SystemState state) { } + CycleData {|#0:Value|}; + } - public void OnUpdate(ref SystemState state) - { - foreach (var component in SystemAPI.Query<{|#0:RefRO>|}>()) - { } + public partial class SomeSystem : SystemBase { + protected override void OnUpdate() { + foreach (var cycleData in SystemAPI.Query()) {} } + }"; - public void OnDestroy(ref SystemState state) { } + var expected = VerifyCS.CompilerError("CS0523").WithLocation(0); + await VerifyCS.VerifySourceGeneratorAsync(source, expected); + } + + [TestMethod] + public async Task SGQC005_QueryingGenericType() + { + const string source = @" + using Unity.Entities; + using Unity.Entities.Tests; + + partial class TranslationSystem : SystemBase where T : struct, IComponentData + { + protected override void OnUpdate() + { + foreach (var aspect in {|#0:SystemAPI.Query>().WithNone()|}) + { + } + } }"; - var expected = VerifyCS.CompilerError(nameof(IdiomaticCSharpForEachCompilerMessages.SGFE010)).WithLocation(0); + var expected = VerifyCS.CompilerError(nameof(QueryConstructionErrors.SGQC005)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } [TestMethod] - public async Task ForEachIteration_WithDataWithCycle() + public async Task SGFE011_QueryingGenericTypeInRef() { const string source = @" using Unity.Entities; using Unity.Entities.Tests; - public struct CycleData : IComponentData + partial class TranslationSystem : SystemBase where T : struct, IComponentData { - CycleData {|#0:Value|}; - } + protected override void OnUpdate() + { + foreach (var component in SystemAPI.Query<{|#0:RefRO|}>()) + { + } + } + }"; + var expected = VerifyCS.CompilerError(nameof(IfeCompilerMessages.SGFE011)).WithLocation(0); + await VerifyCS.VerifySourceGeneratorAsync(source, expected); + } - public partial class SomeSystem : SystemBase { - protected override void OnUpdate() { - foreach (var cycleData in SystemAPI.Query()) {} + [TestMethod] + public async Task SGFE010_QueryingTypeWithGenericTypeArgumentInRef() + { + const string source = @" + using Unity.Entities; + using Unity.Entities.Tests; + + partial class TranslationSystem : SystemBase where T : unmanaged, IComponentData + { + protected override void OnUpdate() + { + foreach (var component in SystemAPI.Query<{|#0:RefRO>|}>()) + { + } } }"; + var expected = VerifyCS.CompilerError(nameof(IfeCompilerMessages.SGFE010)).WithLocation(0); + await VerifyCS.VerifySourceGeneratorAsync(source, expected); + } - var expected = VerifyCS.CompilerError("CS0523").WithLocation(0); + [TestMethod] + public async Task SGFE012_QueryingEnableableType_SpecifyTypeMustBeAbsent() + { + const string source = @" + using Unity.Entities; + using Unity.Entities.Tests; + + partial class TranslationSystem : SystemBase where T : unmanaged, IComponentData + { + protected override void OnUpdate() + { + foreach (var component in SystemAPI.Query>().{|#0:WithAbsent|}()) + { + } + } + }"; + var expected = VerifyCS.CompilerError(nameof(IfeCompilerMessages.SGFE012)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IdiomaticCSharpForEach/ForEachNoErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IdiomaticCSharpForEach/ForEachNoErrorTests.cs index ac67e6e..9d5db9e 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IdiomaticCSharpForEach/ForEachNoErrorTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IdiomaticCSharpForEach/ForEachNoErrorTests.cs @@ -1,5 +1,6 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; using VerifyCS = Unity.Entities.SourceGenerators.Test.CSharpSourceGeneratorVerifier< Unity.Entities.SourceGen.SystemGenerator.SystemGenerator>; @@ -114,4 +115,55 @@ public void OnUpdate(bool unusedParameter, ref SystemState state) { }"; await VerifyCS.VerifySourceGeneratorAsync(source); } + + [TestMethod] + public async Task IdiomaticForEach_WithErrorTypeParam() + { + const string source = @" + using Unity.Entities; + using Unity.Entities.Tests; + using Namespace1; + using Namespace2; + + namespace Namespace1 + { + public struct Data { } + } + + namespace Namespace2 + { + public struct Data { } + } + + public partial struct WithErrorTypeParam : ISystem + { + public void OnUpdate(ref SystemState state) + { + foreach (var data in SystemAPI.Query<{|#0:Data|}>()) {} + } + }"; + + // We are actually expecting an ambiguous type error, but NOT the SGICE we got before + var diagnosticResult = DiagnosticResult.CompilerError("CS0104").WithLocation(0); + await VerifyCS.VerifySourceGeneratorAsync(source, diagnosticResult); + } + + [TestMethod] + public async Task SGFE010_QueryingTypeWithNonGenericTypeArgument() + { + const string source = @" + using Unity.Entities; + using Unity.Entities.Tests; + + partial class TranslationSystem : SystemBase where T : unmanaged, IComponentData + { + protected override void OnUpdate() + { + foreach (var component in SystemAPI.Query<{|#0:RefRO>|}>()) + { + } + } + }"; + await VerifyCS.VerifySourceGeneratorAsync(source); + } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IncrementalGenTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IncrementalGenTests.cs index eba81df..3688410 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IncrementalGenTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/IncrementalGenTests.cs @@ -17,7 +17,9 @@ using Microsoft.CodeAnalysis.Text; using Microsoft.VisualStudio.TestTools.UnitTesting; using Unity.Burst; +using Unity.Collections; using Unity.Entities.Hybrid; +using Unity.Entities.SourceGen.Common; namespace Unity.Entities.SourceGenerators.Test { @@ -50,7 +52,8 @@ static async Task VerifySourceGeneratorAsync(string source, DiagnosticResult[] e => await VerifySourceGeneratorAsync(source, expected, new []{ typeof(EntitiesMock).Assembly, typeof(EntitiesHybridMock).Assembly, - typeof(BurstMock).Assembly + typeof(BurstMock).Assembly, + typeof(Allocator).Assembly }, checksGeneratedSource, generatedFolderName, expectedFileNames); static async Task VerifySourceGeneratorAsync(string source, DiagnosticResult[] expected, IEnumerable additionalAssembliesOverride, bool checksGeneratedSource = true, string generatedFolderName = "Default", params string[] expectedFileNames) diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/LambdaJobs/LambdaJobsErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/LambdaJobs/LambdaJobsErrorTests.cs index 9acf8e4..bd1e8e9 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/LambdaJobs/LambdaJobsErrorTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/LambdaJobs/LambdaJobsErrorTests.cs @@ -1,8 +1,8 @@ using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Testing; using Microsoft.VisualStudio.TestTools.UnitTesting; -using Unity.Entities.SourceGen.Common; -using Unity.Entities.SourceGen.LambdaJobs; -using Unity.Entities.SourceGen.SystemGenerator; +using Unity.Entities.SourceGen.SystemGenerator.Common; +using Unity.Entities.SourceGen.SystemGenerator.LambdaJobs; using VerifyCS = Unity.Entities.SourceGenerators.Test.CSharpSourceGeneratorVerifier< Unity.Entities.SourceGen.SystemGenerator.SystemGenerator>; @@ -13,23 +13,24 @@ namespace Unity.Entities.SourceGenerators; public class LambdaJobsErrorTests { [TestMethod] - public async Task CapturingInvalidIdentifier() + public async Task DC0085_CapturingInvalidIdentifier() { const string source = @" using Unity.Entities; using Unity.Entities.Tests; partial class SomeSystem : SystemBase { - protected override void OnUpdate() + protected override void OnUpdate() { var foo = {|#0:Foo|}(); Entities.ForEach((Entity e) => - { - var blah = foo.bar(); - }).Schedule(); + {|#1:{ + var blah = foo; + }|}).Schedule(); } }"; - var expected = VerifyCS.CompilerError("CS0103").WithLocation(0); - await VerifyCS.VerifySourceGeneratorAsync(source, expected); + var diagnosticResult1 = DiagnosticResult.CompilerError("CS0103").WithLocation(0); + var diagnosticResult2 = DiagnosticResult.CompilerError(nameof(LambdaJobsErrors.DC0086)).WithLocation(1); + await VerifyCS.VerifySourceGeneratorAsync(source, diagnosticResult1, diagnosticResult2); } [TestMethod] diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/LambdaJobs/LambdaJobsVerifyTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/LambdaJobs/LambdaJobsVerifyTests.cs index 77db4d2..41a0083 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/LambdaJobs/LambdaJobsVerifyTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/LambdaJobs/LambdaJobsVerifyTests.cs @@ -13,34 +13,33 @@ public class LambdaJobsVerifyTests public async Task BasicEFE() { const string source = @" - using Unity.Burst; - using Unity.Entities; - using Unity.Entities.Tests; +using Unity.Burst; +using Unity.Entities; +using Unity.Entities.Tests; - public struct TestData : IComponentData - { - public int value; - } +public struct TestData : IComponentData +{ + public int value; +} - public partial class BasicEFESystem : SystemBase +public partial class BasicEFESystem : SystemBase +{ + protected override void OnUpdate() + { + Entities.ForEach((ref TestData testData) => { - protected override void OnUpdate() - { - Entities.ForEach((ref TestData testData) => - { - testData.value++; - }) - .WithBurst(Unity.Burst.FloatMode.Deterministic, Unity.Burst.FloatPrecision.Low, true) - .ScheduleParallel(); + testData.value++; + }) + .WithBurst(Unity.Burst.FloatMode.Deterministic, Unity.Burst.FloatPrecision.Low, true) + .ScheduleParallel(); - Entities.ForEach((ref TestData testData) => - { - testData.value++; - }) - .ScheduleParallel(); - } - } - "; + Entities.ForEach((ref TestData testData) => + { + testData.value++; + }) + .ScheduleParallel(); + } +}"; await VerifyCS.VerifySourceGeneratorAsync(source, nameof(BasicEFE), "Test0__System_19875963020.g.cs"); } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SourceGenTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SourceGenTests.cs index 579fe39..69049d4 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SourceGenTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SourceGenTests.cs @@ -19,6 +19,7 @@ using Unity.Burst; using Unity.Collections; using Unity.Entities.Hybrid; +using Unity.Entities.SourceGen.Common; namespace Unity.Entities.SourceGenerators.Test { diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPI/SystemAPIErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPI/SystemAPIErrorTests.cs index a9ce1a0..96bc671 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPI/SystemAPIErrorTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPI/SystemAPIErrorTests.cs @@ -27,7 +27,7 @@ public void Idk() where T:struct,IComponentData{ var hadComp = {|#0:HasComponent(default)|}; } }"; - var expected = VerifyCS.CompilerError(nameof(SystemAPIErrors.SGSA0001)).WithLocation(0); + var expected = VerifyCS.CompilerError(nameof(SystemApiContextErrors.SGSA0001)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } @@ -45,7 +45,7 @@ public void OnUpdate(ref SystemState state) { var lookup = {|#0:GetComponentLookup(ro)|}; } }"; - var expected = VerifyCS.CompilerError(nameof(SystemAPIErrors.SGSA0002)).WithLocation(0); + var expected = VerifyCS.CompilerError(nameof(SystemApiContextErrors.SGSA0002)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPI/SystemAPIVerifyTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPI/SystemAPIVerifyTests.cs index 91e267c..179b2da 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPI/SystemAPIVerifyTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPI/SystemAPIVerifyTests.cs @@ -12,7 +12,7 @@ public class SystemAPIVerifyTests [TestMethod] public async Task SystemMethodWithComponentAccessInvocation() { - var testSource = @" + const string testSource = @" using Unity.Burst; using Unity.Entities; using Unity.Entities.Tests; @@ -46,7 +46,7 @@ public void OnUpdate(ref SystemState state) [TestMethod] public async Task SystemMethodWithBufferAccessInvocation() { - var testSource = @" + const string testSource = @" using Unity.Burst; using Unity.Entities; using Unity.Entities.Tests; @@ -86,7 +86,7 @@ public void OnUpdate(ref SystemState state) [TestMethod] public async Task SystemMethodWithStorageInfoInvocation() { - var testSource = @" + const string testSource = @" using Unity.Burst; using Unity.Entities; using Unity.Entities.Tests; @@ -122,7 +122,7 @@ public void OnUpdate(ref SystemState state) [TestMethod] public async Task SystemMethodWithAspectInvocation() { - var testSource = @" + const string testSource = @" using Unity.Burst; using Unity.Entities; using Unity.Entities.Tests; @@ -149,20 +149,20 @@ public void OnUpdate(ref SystemState state) [TestMethod] public async Task SystemMethodWithManagedComponent() { - var testSource = @" - using Unity.Burst; - using Unity.Entities; - using Unity.Entities.Tests; - using static Unity.Entities.SystemAPI; - public partial struct SomeSystem : ISystem { - public void OnCreate(ref SystemState state){} - public void OnDestroy(ref SystemState state){} - public void OnUpdate(ref SystemState state){ - var e = state.EntityManager.CreateEntity(); - state.EntityManager.AddComponentData(e, new EcsTestManagedComponent{value = ""cake""}); - var comp = SystemAPI.ManagedAPI.GetSingleton().value; - } + const string testSource = @" +using Unity.Burst; +using Unity.Entities; +using Unity.Entities.Tests; +using static Unity.Entities.SystemAPI; +public partial struct SomeSystem : ISystem { + public void OnCreate(ref SystemState state){} + public void OnDestroy(ref SystemState state){} + public void OnUpdate(ref SystemState state){ + var e = state.EntityManager.CreateEntity(); + state.EntityManager.AddComponentData(e, new EcsTestManagedComponent{value = ""cake""}); + var comp = SystemAPI.ManagedAPI.GetSingleton().value; } +} "; await VerifyCS.VerifySourceGeneratorAsync(testSource, nameof(SystemMethodWithManagedComponent), "Test0__System_19875963020.g.cs"); @@ -171,31 +171,55 @@ public void OnUpdate(ref SystemState state){ [TestMethod] public async Task SystemAPIInPartialMethod() { - var testSource = @" - using Unity.Burst; - using Unity.Entities; - using Unity.Entities.Tests; + const string testSource = @" +using Unity.Burst; +using Unity.Entities; +using Unity.Entities.Tests; - public unsafe partial struct PartialMethodSystem +public unsafe partial struct PartialMethodSystem +{ + partial void CustomOnUpdate(ref SystemState state) { - partial void CustomOnUpdate(ref SystemState state) - { - var tickSingleton2 = SystemAPI.GetSingleton(); - } + var tickSingleton2 = SystemAPI.GetSingleton(); } +} - public unsafe partial struct PartialMethodSystem : ISystem +public unsafe partial struct PartialMethodSystem : ISystem +{ + public void OnUpdate(ref SystemState state) { - public void OnUpdate(ref SystemState state) - { - CustomOnUpdate(ref state); - } - - partial void CustomOnUpdate(ref SystemState state); + CustomOnUpdate(ref state); } + + partial void CustomOnUpdate(ref SystemState state); +} "; await VerifyCS.VerifySourceGeneratorAsync(testSource, nameof(SystemAPIInPartialMethod), "Test0__System_19875963020.g.cs"); } + + [TestMethod] + public async Task NestedSystemAPIInvocation() + { + const string testSource = @" +using Unity.Burst; +using Unity.Entities; +using Unity.Entities.Tests; + +public unsafe partial struct PartialMethodSystem : ISystem +{ + public void OnUpdate(ref SystemState state) + { + ToggleEnabled(default(Entity), ref state); + } + + void ToggleEnabled(Entity entity, ref SystemState state) + { + SystemAPI.SetComponentEnabled(entity, !SystemAPI.IsComponentEnabled(entity)); + } +}"; + + await VerifyCS.VerifySourceGeneratorAsync(testSource, nameof(NestedSystemAPIInvocation), "Test0__System_19875963020.g.cs"); + } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPIQueryBuilderErrorTests/SystemAPIQueryBuilderErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPIQueryBuilderErrorTests/SystemAPIQueryBuilderErrorTests.cs index 63bcb7d..97e7595 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPIQueryBuilderErrorTests/SystemAPIQueryBuilderErrorTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemAPIQueryBuilderErrorTests/SystemAPIQueryBuilderErrorTests.cs @@ -1,5 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Threading.Tasks; +using Unity.Entities.SourceGen.Common; +using Unity.Entities.SourceGen.SystemGenerator.Common; using Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder; using VerifyCS = Unity.Entities.SourceGenerators.Test.CSharpSourceGeneratorVerifier< @@ -24,7 +26,25 @@ protected override void OnUpdate() EntityManager.AddComponent(query); } }"; - var expected = VerifyCS.CompilerError(nameof(SystemAPIQueryBuilderErrors.SGQB001)).WithLocation(0); + var expected = VerifyCS.CompilerError(nameof(SystemApiQueryBuilderErrors.SGQB001)).WithLocation(0); + await VerifyCS.VerifySourceGeneratorAsync(source, expected); + } + + [TestMethod] + public async Task SGQC005_WithGenericTypeArgument() + { + const string source = @" + using Unity.Entities; + using Unity.Entities.Tests; + partial class SomeSystem : SystemBase where T : struct, IComponentData + { + protected override void OnUpdate() + { + var query = {|#0:SystemAPI.QueryBuilder().WithAll().Build()|}; + EntityManager.AddComponent(query); + } + }"; + var expected = VerifyCS.CompilerError(nameof(QueryConstructionErrors.SGQC005)).WithLocation(0); await VerifyCS.VerifySourceGeneratorAsync(source, expected); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemGenerator/SystemGeneratorErrorTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemGenerator/SystemGeneratorErrorTests.cs index f4ff4b2..c93bccc 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemGenerator/SystemGeneratorErrorTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemGenerator/SystemGeneratorErrorTests.cs @@ -1,6 +1,7 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System.Threading.Tasks; using Unity.Entities.SourceGen.SystemGenerator; +using Unity.Entities.SourceGen.SystemGenerator.Common; using VerifyCS = Unity.Entities.SourceGenerators.Test.CSharpSourceGeneratorVerifier< Unity.Entities.SourceGen.SystemGenerator.SystemGenerator>; diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemGenerator/SystemGeneratorVerifyTests.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemGenerator/SystemGeneratorVerifyTests.cs index c075691..a16f207 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemGenerator/SystemGeneratorVerifyTests.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/SystemGenerator/SystemGeneratorVerifyTests.cs @@ -12,22 +12,22 @@ public class SystemGeneratorVerifyTests [TestMethod] public async Task EntitiesForEachNonCapturing() { - var testSource = @" - using Unity.Entities; - using Unity.Mathematics; - using Unity.Burst; + const string testSource = @" +using Unity.Entities; +using Unity.Mathematics; +using Unity.Burst; - struct Translation : IComponentData { public float Value; } - struct TagComponent1 : IComponentData {} - struct TagComponent2 : IComponentData {} +struct Translation : IComponentData { public float Value; } +struct TagComponent1 : IComponentData {} +struct TagComponent2 : IComponentData {} - partial class EntitiesForEachNonCapturing : SystemBase - { - protected override void OnUpdate() - { - Entities.ForEach((ref Translation translation, ref TagComponent1 tag1, in TagComponent2 tag2) => { translation.Value += 5; }).Run(); - } - }"; +partial class EntitiesForEachNonCapturing : SystemBase +{ + protected override void OnUpdate() + { + Entities.ForEach((ref Translation translation, ref TagComponent1 tag1, in TagComponent2 tag2) => { translation.Value += 5; }).Run(); + } +}"; await VerifyCS.VerifySourceGeneratorAsync(testSource, nameof(EntitiesForEachNonCapturing), "Test0__System_19875963020.g.cs"); } @@ -35,18 +35,18 @@ protected override void OnUpdate() [TestMethod] public async Task EntitiesForEachCachesBufferTypeHandle() { - var testSource = @" - using Unity.Entities; + const string testSource = @" +using Unity.Entities; - struct BufferData : IBufferElementData { public float Value; } +struct BufferData : IBufferElementData { public float Value; } - partial class EntitiesForEachDynamicBuffer : SystemBase - { - protected override void OnUpdate() - { - Entities.ForEach((DynamicBuffer buf) => { }).Run(); - } - }"; +partial class EntitiesForEachDynamicBuffer : SystemBase +{ + protected override void OnUpdate() + { + Entities.ForEach((DynamicBuffer buf) => { }).Run(); + } +}"; await VerifyCS.VerifySourceGeneratorAsync(testSource, nameof(EntitiesForEachCachesBufferTypeHandle), "Test0__System_19875963020.g.cs"); } } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/Unity.Entities.SourceGen.Tests.csproj b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/Unity.Entities.SourceGen.Tests.csproj index 114a47e..d27c204 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/Unity.Entities.SourceGen.Tests.csproj +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/Unity.Entities.SourceGen.Tests.csproj @@ -5,11 +5,13 @@ net6.0 enable false - Unity.Entities.SourceGen.Tests + True + AnyCPU + diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectComplex/Test0__Aspect_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectComplex/Test0__Aspect_19875963020.g.cs index 047ac0e..905d8bd 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectComplex/Test0__Aspect_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectComplex/Test0__Aspect_19875963020.g.cs @@ -56,7 +56,7 @@ public static void AddRequiredComponentTypes(ref global::System.Span. /// Equivalent to but for aspect types. /// Constructed from an system state via its constructor. - public struct Lookup + public struct Lookup : global::Unity.Entities.Internal.InternalCompilerInterface.IAspectLookup { global::Unity.Entities.ComponentLookup AspectSimple_DataCAc; @@ -237,7 +237,7 @@ public AspectSimple Current { /// So it completes all write dependencies of the components, buffers, etc. to allow for reading. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); } @@ -248,7 +248,7 @@ public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemS /// and it completes all read dependencies, so we can write to it. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRW(); } @@ -339,7 +339,7 @@ public static void AddRequiredComponentTypes(ref global::System.Span. /// Equivalent to but for aspect types. /// Constructed from an system state via its constructor. - public struct Lookup + public struct Lookup : global::Unity.Entities.Internal.InternalCompilerInterface.IAspectLookup { global::Unity.Entities.EntityStorageInfoLookup _m_Esil; global::Unity.Entities.BufferLookup Aspect2_DynamicBufferBAc; @@ -629,7 +629,7 @@ public Aspect2 Current { /// So it completes all write dependencies of the components, buffers, etc. to allow for reading. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); state.EntityManager.CompleteDependencyBeforeRO(); @@ -646,7 +646,7 @@ public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemS /// and it completes all read dependencies, so we can write to it. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRW(); state.EntityManager.CompleteDependencyBeforeRW(); @@ -753,7 +753,7 @@ public static void AddRequiredComponentTypes(ref global::System.Span. /// Equivalent to but for aspect types. /// Constructed from an system state via its constructor. - public struct Lookup + public struct Lookup : global::Unity.Entities.Internal.InternalCompilerInterface.IAspectLookup { global::Unity.Entities.EntityStorageInfoLookup _m_Esil; global::Unity.Entities.BufferLookup AspectNestedAliasing_DynamicBuffer; @@ -1043,7 +1043,7 @@ public AspectNestedAliasing Current { /// So it completes all write dependencies of the components, buffers, etc. to allow for reading. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); state.EntityManager.CompleteDependencyBeforeRO(); @@ -1060,7 +1060,7 @@ public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemS /// and it completes all read dependencies, so we can write to it. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRW(); state.EntityManager.CompleteDependencyBeforeRW(); diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectEFE/Test0__Aspect_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectEFE/Test0__Aspect_19875963020.g.cs index 9a71644..6344557 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectEFE/Test0__Aspect_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectEFE/Test0__Aspect_19875963020.g.cs @@ -56,7 +56,7 @@ public static void AddRequiredComponentTypes(ref global::System.Span. /// Equivalent to but for aspect types. /// Constructed from an system state via its constructor. - public struct Lookup + public struct Lookup : global::Unity.Entities.Internal.InternalCompilerInterface.IAspectLookup { global::Unity.Entities.ComponentLookup MyAspectEFE_DataCAc; @@ -237,7 +237,7 @@ public MyAspectEFE Current { /// So it completes all write dependencies of the components, buffers, etc. to allow for reading. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); } @@ -248,7 +248,7 @@ public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemS /// and it completes all read dependencies, so we can write to it. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRW(); } @@ -308,7 +308,7 @@ public static void AddRequiredComponentTypes(ref global::System.Span. /// Equivalent to but for aspect types. /// Constructed from an system state via its constructor. - public struct Lookup + public struct Lookup : global::Unity.Entities.Internal.InternalCompilerInterface.IAspectLookup { global::Unity.Entities.ComponentLookup MyAspectEFE2_DataCAc; @@ -489,7 +489,7 @@ public MyAspectEFE2 Current { /// So it completes all write dependencies of the components, buffers, etc. to allow for reading. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); } @@ -500,7 +500,7 @@ public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemS /// and it completes all read dependencies, so we can write to it. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRW(); } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectSimple/Test0__Aspect_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectSimple/Test0__Aspect_19875963020.g.cs index c7b3695..9ad5929 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectSimple/Test0__Aspect_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectSimple/Test0__Aspect_19875963020.g.cs @@ -56,7 +56,7 @@ public static void AddRequiredComponentTypes(ref global::System.Span. /// Equivalent to but for aspect types. /// Constructed from an system state via its constructor. - public struct Lookup + public struct Lookup : global::Unity.Entities.Internal.InternalCompilerInterface.IAspectLookup { global::Unity.Entities.ComponentLookup AspectSimple_DataCAc; @@ -237,7 +237,7 @@ public AspectSimple Current { /// So it completes all write dependencies of the components, buffers, etc. to allow for reading. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); } @@ -248,7 +248,7 @@ public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemS /// and it completes all read dependencies, so we can write to it. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRW(); } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectTypeShadowing/Test0__Aspect_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectTypeShadowing/Test0__Aspect_19875963020.g.cs index a6ea67d..575bc74 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectTypeShadowing/Test0__Aspect_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectTypeShadowing/Test0__Aspect_19875963020.g.cs @@ -55,7 +55,7 @@ public static void AddRequiredComponentTypes(ref global::System.Span. /// Equivalent to but for aspect types. /// Constructed from an system state via its constructor. - public struct Lookup + public struct Lookup : global::Unity.Entities.Internal.InternalCompilerInterface.IAspectLookup { global::Unity.Entities.ComponentLookup MyAspect_EcsTestDataCAc; @@ -236,7 +236,7 @@ public MyAspect Current { /// So it completes all write dependencies of the components, buffers, etc. to allow for reading. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); } @@ -247,7 +247,7 @@ public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemS /// and it completes all read dependencies, so we can write to it. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRW(); } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectUsing/Test0__Aspect_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectUsing/Test0__Aspect_19875963020.g.cs index a6ea67d..575bc74 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectUsing/Test0__Aspect_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/AspectUsing/Test0__Aspect_19875963020.g.cs @@ -55,7 +55,7 @@ public static void AddRequiredComponentTypes(ref global::System.Span. /// Equivalent to but for aspect types. /// Constructed from an system state via its constructor. - public struct Lookup + public struct Lookup : global::Unity.Entities.Internal.InternalCompilerInterface.IAspectLookup { global::Unity.Entities.ComponentLookup MyAspect_EcsTestDataCAc; @@ -236,7 +236,7 @@ public MyAspect Current { /// So it completes all write dependencies of the components, buffers, etc. to allow for reading. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); } @@ -247,7 +247,7 @@ public static void CompleteDependencyBeforeRO(ref global::Unity.Entities.SystemS /// and it completes all read dependencies, so we can write to it. /// /// The containing an storing all dependencies. - public static void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) + public void CompleteDependencyBeforeRW(ref global::Unity.Entities.SystemState state) { state.EntityManager.CompleteDependencyBeforeRW(); } diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/BasicEFE/Test0__System_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/BasicEFE/Test0__System_19875963020.g.cs index be012db..88172c2 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/BasicEFE/Test0__System_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/BasicEFE/Test0__System_19875963020.g.cs @@ -3,41 +3,44 @@ using Unity.Burst; using Unity.Entities; using Unity.Entities.Tests; - [global::System.Runtime.CompilerServices.CompilerGenerated] -public partial class BasicEFESystem : global::Unity.Entities.SystemBase +public partial class BasicEFESystem { - [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate")] - void __OnUpdate_1817F1CB() + [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_T0")] + void __OnUpdate_450AADF4() { #line 15 "/0/Test0.cs" - BasicEFESystem_123EB51D_LambdaJob_0_Execute(); + + BasicEFESystem_6F759C06_LambdaJob_0_Execute(); #line 22 "/0/Test0.cs" - BasicEFESystem_123EB51D_LambdaJob_1_Execute(); + + BasicEFESystem_6F759C06_LambdaJob_1_Execute(); } - #line 20 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" + #line 21 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" [global::Unity.Burst.NoAlias] - [global::Unity.Burst.BurstCompile(FloatMode = global::Unity.Burst.FloatMode.Deterministic, FloatPrecision = global::Unity.Burst.FloatPrecision.Low, CompileSynchronously = true)] - struct BasicEFESystem_123EB51D_LambdaJob_0_Job : global::Unity.Entities.IJobChunk + [global::Unity.Burst.BurstCompile( + FloatMode=global::Unity.Burst.FloatMode.Deterministic, FloatPrecision=global::Unity.Burst.FloatPrecision.Low, CompileSynchronously=true)] + struct BasicEFESystem_6F759C06_LambdaJob_0_Job : global::Unity.Entities.IJobChunk { public global::Unity.Entities.ComponentTypeHandle __testDataTypeHandle; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void OriginalLambdaBody([Unity.Burst.NoAlias] ref global::TestData testData) { - #line 17 "/0/Test0.cs" - testData.value++; - } - - #line 33 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" +#line 17 "/0/Test0.cs" +testData.value++; + } + #line 35 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" [global::System.Runtime.CompilerServices.CompilerGenerated] public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchIndex, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask) { + #line 39 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" var testDataArrayPtr = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr(chunk, ref __testDataTypeHandle); int chunkEntityCount = chunk.Count; if (!useEnabledMask) { - for (var entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex) + for(var entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex) { OriginalLambdaBody(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement(testDataArrayPtr, entityIndex)); } @@ -50,7 +53,7 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd { int entityIndex = 0; int batchEndIndex = 0; - while (global::Unity.Entities.EnabledBitUtility.TryGetNextRange(chunkEnabledMask, batchEndIndex, out entityIndex, out batchEndIndex)) + while (global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeTryGetNextEnabledBitRange(chunkEnabledMask, batchEndIndex, out entityIndex, out batchEndIndex)) { while (entityIndex < batchEndIndex) { @@ -69,10 +72,8 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd { OriginalLambdaBody(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement(testDataArrayPtr, entityIndex)); } - mask64 >>= 1; } - mask64 = chunkEnabledMask.ULong1; for (var entityIndex = 64; entityIndex < chunkEntityCount; ++entityIndex) { @@ -80,43 +81,45 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd { OriginalLambdaBody(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement(testDataArrayPtr, entityIndex)); } - mask64 >>= 1; } } } } } - - void BasicEFESystem_123EB51D_LambdaJob_0_Execute() + void BasicEFESystem_6F759C06_LambdaJob_0_Execute() { __TypeHandle.__TestData_RW_ComponentTypeHandle.Update(ref this.CheckedStateRef); - var __job = new BasicEFESystem_123EB51D_LambdaJob_0_Job{__testDataTypeHandle = __TypeHandle.__TestData_RW_ComponentTypeHandle}; + var __job = new BasicEFESystem_6F759C06_LambdaJob_0_Job + { + __testDataTypeHandle = __TypeHandle.__TestData_RW_ComponentTypeHandle + }; + this.CheckedStateRef.Dependency = global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.ScheduleParallel(__job, __query_1641826536_0, this.CheckedStateRef.Dependency); } - - #line 99 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" + #line 101 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" [global::Unity.Burst.NoAlias] [global::Unity.Burst.BurstCompile] - struct BasicEFESystem_123EB51D_LambdaJob_1_Job : global::Unity.Entities.IJobChunk + struct BasicEFESystem_6F759C06_LambdaJob_1_Job : global::Unity.Entities.IJobChunk { public global::Unity.Entities.ComponentTypeHandle __testDataTypeHandle; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void OriginalLambdaBody([Unity.Burst.NoAlias] ref global::TestData testData) { - #line 24 "/0/Test0.cs" - testData.value++; - } - - #line 112 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" +#line 24 "/0/Test0.cs" +testData.value++; + } + #line 114 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" [global::System.Runtime.CompilerServices.CompilerGenerated] public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchIndex, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask) { + #line 118 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" var testDataArrayPtr = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr(chunk, ref __testDataTypeHandle); int chunkEntityCount = chunk.Count; if (!useEnabledMask) { - for (var entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex) + for(var entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex) { OriginalLambdaBody(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement(testDataArrayPtr, entityIndex)); } @@ -129,7 +132,7 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd { int entityIndex = 0; int batchEndIndex = 0; - while (global::Unity.Entities.EnabledBitUtility.TryGetNextRange(chunkEnabledMask, batchEndIndex, out entityIndex, out batchEndIndex)) + while (global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeTryGetNextEnabledBitRange(chunkEnabledMask, batchEndIndex, out entityIndex, out batchEndIndex)) { while (entityIndex < batchEndIndex) { @@ -148,10 +151,8 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd { OriginalLambdaBody(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement(testDataArrayPtr, entityIndex)); } - mask64 >>= 1; } - mask64 = chunkEnabledMask.ULong1; for (var entityIndex = 64; entityIndex < chunkEntityCount; ++entityIndex) { @@ -159,21 +160,23 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd { OriginalLambdaBody(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement(testDataArrayPtr, entityIndex)); } - mask64 >>= 1; } } } } } - - void BasicEFESystem_123EB51D_LambdaJob_1_Execute() + void BasicEFESystem_6F759C06_LambdaJob_1_Execute() { __TypeHandle.__TestData_RW_ComponentTypeHandle.Update(ref this.CheckedStateRef); - var __job = new BasicEFESystem_123EB51D_LambdaJob_1_Job{__testDataTypeHandle = __TypeHandle.__TestData_RW_ComponentTypeHandle}; + var __job = new BasicEFESystem_6F759C06_LambdaJob_1_Job + { + __testDataTypeHandle = __TypeHandle.__TestData_RW_ComponentTypeHandle + }; + this.CheckedStateRef.Dependency = global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.ScheduleParallel(__job, __query_1641826536_0, this.CheckedStateRef.Dependency); } - + TypeHandle __TypeHandle; global::Unity.Entities.EntityQuery __query_1641826536_0; struct TypeHandle @@ -184,18 +187,24 @@ public void __AssignHandles(ref global::Unity.Entities.SystemState state) { __TestData_RW_ComponentTypeHandle = state.GetComponentTypeHandle(false); } + } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void __AssignQueries(ref global::Unity.Entities.SystemState state) { - __query_1641826536_0 = state.GetEntityQuery(new global::Unity.Entities.EntityQueryDesc{All = new global::Unity.Entities.ComponentType[]{global::Unity.Entities.ComponentType.ReadWrite()}, Any = new global::Unity.Entities.ComponentType[]{}, None = new global::Unity.Entities.ComponentType[]{}, Disabled = new global::Unity.Entities.ComponentType[]{}, Absent = new global::Unity.Entities.ComponentType[]{}, Options = global::Unity.Entities.EntityQueryOptions.Default}); + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + __query_1641826536_0 = + entityQueryBuilder + .WithAllRW() + .Build(ref state); + entityQueryBuilder.Reset(); + entityQueryBuilder.Dispose(); } - + protected override void OnCreateForCompiler() { base.OnCreateForCompiler(); __AssignQueries(ref this.CheckedStateRef); __TypeHandle.__AssignHandles(ref this.CheckedStateRef); } -} \ No newline at end of file +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/EntitiesForEachCachesBufferTypeHandle/Test0__System_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/EntitiesForEachCachesBufferTypeHandle/Test0__System_19875963020.g.cs index 34cb618..00d31c5 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/EntitiesForEachCachesBufferTypeHandle/Test0__System_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/EntitiesForEachCachesBufferTypeHandle/Test0__System_19875963020.g.cs @@ -1,39 +1,39 @@ #pragma warning disable 0219 #line 1 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" using Unity.Entities; - [global::System.Runtime.CompilerServices.CompilerGenerated] -partial class EntitiesForEachDynamicBuffer : global::Unity.Entities.SystemBase +partial class EntitiesForEachDynamicBuffer { - [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate")] - void __OnUpdate_1817F1CB() + [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_T0")] + void __OnUpdate_450AADF4() { #line 10 "/0/Test0.cs" - EntitiesForEachDynamicBuffer_2C1FEB3C_LambdaJob_0_Execute(); + + EntitiesForEachDynamicBuffer_7418F297_LambdaJob_0_Execute(); } #line 16 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" [global::Unity.Burst.NoAlias] [global::Unity.Burst.BurstCompile] - struct EntitiesForEachDynamicBuffer_2C1FEB3C_LambdaJob_0_Job : global::Unity.Entities.IJobChunk + struct EntitiesForEachDynamicBuffer_7418F297_LambdaJob_0_Job : global::Unity.Entities.IJobChunk { internal static global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate FunctionPtrFieldNoBurst; internal static global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate FunctionPtrFieldBurst; public BufferTypeHandle __bufTypeHandle; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void OriginalLambdaBody(DynamicBuffer buf) - { - } - - #line 29 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" + { } + #line 28 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" [global::System.Runtime.CompilerServices.CompilerGenerated] public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchIndex, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask) { + #line 32 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" var bufAccessor = chunk.GetBufferAccessor(ref __bufTypeHandle); int chunkEntityCount = chunk.Count; if (!useEnabledMask) { - for (var entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex) + for(var entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex) { OriginalLambdaBody(bufAccessor[entityIndex]); } @@ -46,7 +46,7 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd { int entityIndex = 0; int batchEndIndex = 0; - while (global::Unity.Entities.EnabledBitUtility.TryGetNextRange(chunkEnabledMask, batchEndIndex, out entityIndex, out batchEndIndex)) + while (global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeTryGetNextEnabledBitRange(chunkEnabledMask, batchEndIndex, out entityIndex, out batchEndIndex)) { while (entityIndex < batchEndIndex) { @@ -65,10 +65,8 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd { OriginalLambdaBody(bufAccessor[entityIndex]); } - mask64 >>= 1; } - mask64 = chunkEnabledMask.ULong1; for (var entityIndex = 64; entityIndex < chunkEntityCount; ++entityIndex) { @@ -76,39 +74,40 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd { OriginalLambdaBody(bufAccessor[entityIndex]); } - mask64 >>= 1; } } } } - [global::Unity.Burst.BurstCompile] [global::AOT.MonoPInvokeCallback(typeof(global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate))] public static void RunWithoutJobSystem(ref global::Unity.Entities.EntityQuery query, global::System.IntPtr jobPtr) { try { - global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.RunWithoutJobsInternal(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeAsRef(jobPtr), ref query); + global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.RunWithoutJobsInternal(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeAsRef(jobPtr), ref query); } finally { } } } - - void EntitiesForEachDynamicBuffer_2C1FEB3C_LambdaJob_0_Execute() + void EntitiesForEachDynamicBuffer_7418F297_LambdaJob_0_Execute() { __TypeHandle.__BufferData_RW_BufferTypeHandle.Update(ref this.CheckedStateRef); - var __job = new EntitiesForEachDynamicBuffer_2C1FEB3C_LambdaJob_0_Job{__bufTypeHandle = __TypeHandle.__BufferData_RW_BufferTypeHandle}; - if (!__query_1641826531_0.IsEmptyIgnoreFilter) + var __job = new EntitiesForEachDynamicBuffer_7418F297_LambdaJob_0_Job + { + __bufTypeHandle = __TypeHandle.__BufferData_RW_BufferTypeHandle + }; + + if(!__query_1641826531_0.IsEmptyIgnoreFilter) { this.CheckedStateRef.CompleteDependency(); - var __functionPointer = global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobCompilerEnabled ? EntitiesForEachDynamicBuffer_2C1FEB3C_LambdaJob_0_Job.FunctionPtrFieldBurst : EntitiesForEachDynamicBuffer_2C1FEB3C_LambdaJob_0_Job.FunctionPtrFieldNoBurst; + var __functionPointer = global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobCompilerEnabled ? EntitiesForEachDynamicBuffer_7418F297_LambdaJob_0_Job.FunctionPtrFieldBurst : EntitiesForEachDynamicBuffer_7418F297_LambdaJob_0_Job.FunctionPtrFieldNoBurst; global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeRunJobChunk(ref __job, __query_1641826531_0, __functionPointer); } } - + TypeHandle __TypeHandle; global::Unity.Entities.EntityQuery __query_1641826531_0; struct TypeHandle @@ -119,20 +118,26 @@ public void __AssignHandles(ref global::Unity.Entities.SystemState state) { __BufferData_RW_BufferTypeHandle = state.GetBufferTypeHandle(false); } + } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void __AssignQueries(ref global::Unity.Entities.SystemState state) { - __query_1641826531_0 = state.GetEntityQuery(new global::Unity.Entities.EntityQueryDesc{All = new global::Unity.Entities.ComponentType[]{global::Unity.Entities.ComponentType.ReadWrite()}, Any = new global::Unity.Entities.ComponentType[]{}, None = new global::Unity.Entities.ComponentType[]{}, Disabled = new global::Unity.Entities.ComponentType[]{}, Absent = new global::Unity.Entities.ComponentType[]{}, Options = global::Unity.Entities.EntityQueryOptions.Default}); + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + __query_1641826531_0 = + entityQueryBuilder + .WithAllRW() + .Build(ref state); + entityQueryBuilder.Reset(); + entityQueryBuilder.Dispose(); } - + protected override void OnCreateForCompiler() { base.OnCreateForCompiler(); __AssignQueries(ref this.CheckedStateRef); __TypeHandle.__AssignHandles(ref this.CheckedStateRef); - EntitiesForEachDynamicBuffer_2C1FEB3C_LambdaJob_0_Job.FunctionPtrFieldNoBurst = EntitiesForEachDynamicBuffer_2C1FEB3C_LambdaJob_0_Job.RunWithoutJobSystem; - EntitiesForEachDynamicBuffer_2C1FEB3C_LambdaJob_0_Job.FunctionPtrFieldBurst = Unity.Entities.Internal.InternalCompilerInterface.BurstCompile(EntitiesForEachDynamicBuffer_2C1FEB3C_LambdaJob_0_Job.FunctionPtrFieldNoBurst); + EntitiesForEachDynamicBuffer_7418F297_LambdaJob_0_Job.FunctionPtrFieldNoBurst = EntitiesForEachDynamicBuffer_7418F297_LambdaJob_0_Job.RunWithoutJobSystem; + EntitiesForEachDynamicBuffer_7418F297_LambdaJob_0_Job.FunctionPtrFieldBurst = Unity.Entities.Internal.InternalCompilerInterface.BurstCompile(EntitiesForEachDynamicBuffer_7418F297_LambdaJob_0_Job.FunctionPtrFieldNoBurst); } -} \ No newline at end of file +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/EntitiesForEachNonCapturing/Test0__System_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/EntitiesForEachNonCapturing/Test0__System_19875963020.g.cs index c299a04..bc534ed 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/EntitiesForEachNonCapturing/Test0__System_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/EntitiesForEachNonCapturing/Test0__System_19875963020.g.cs @@ -3,44 +3,45 @@ using Unity.Entities; using Unity.Mathematics; using Unity.Burst; - [global::System.Runtime.CompilerServices.CompilerGenerated] -partial class EntitiesForEachNonCapturing : global::Unity.Entities.SystemBase +partial class EntitiesForEachNonCapturing { - [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate")] - void __OnUpdate_1817F1CB() + [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_T0")] + void __OnUpdate_450AADF4() { #line 14 "/0/Test0.cs" - EntitiesForEachNonCapturing_625D99F_LambdaJob_0_Execute(); + + EntitiesForEachNonCapturing_4E2AFFBE_LambdaJob_0_Execute(); } #line 18 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" [global::Unity.Burst.NoAlias] [global::Unity.Burst.BurstCompile] - struct EntitiesForEachNonCapturing_625D99F_LambdaJob_0_Job : global::Unity.Entities.IJobChunk + struct EntitiesForEachNonCapturing_4E2AFFBE_LambdaJob_0_Job : global::Unity.Entities.IJobChunk { internal static global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate FunctionPtrFieldNoBurst; internal static global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate FunctionPtrFieldBurst; public global::Unity.Entities.ComponentTypeHandle __translationTypeHandle; + + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void OriginalLambdaBody([Unity.Burst.NoAlias] ref global::Translation translation, [Unity.Burst.NoAlias] ref global::TagComponent1 tag1, [Unity.Burst.NoAlias] in global::TagComponent2 tag2) - { - #line 14 "/0/Test0.cs" - translation.Value += 5; - } - + { +#line 14 "/0/Test0.cs" +translation.Value += 5; } #line 33 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" [global::System.Runtime.CompilerServices.CompilerGenerated] public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchIndex, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask) { + #line 37 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" var translationArrayPtr = global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetChunkNativeArrayIntPtr(chunk, ref __translationTypeHandle); int chunkEntityCount = chunk.Count; if (!useEnabledMask) { - for (var entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex) + for(var entityIndex = 0; entityIndex < chunkEntityCount; ++entityIndex) { global::TagComponent1 tag1 = default; - ; global::TagComponent2 tag2 = default; OriginalLambdaBody(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement(translationArrayPtr, entityIndex), ref tag1, in tag2); } @@ -53,12 +54,11 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd { int entityIndex = 0; int batchEndIndex = 0; - while (global::Unity.Entities.EnabledBitUtility.TryGetNextRange(chunkEnabledMask, batchEndIndex, out entityIndex, out batchEndIndex)) + while (global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeTryGetNextEnabledBitRange(chunkEnabledMask, batchEndIndex, out entityIndex, out batchEndIndex)) { while (entityIndex < batchEndIndex) { global::TagComponent1 tag1 = default; - ; global::TagComponent2 tag2 = default; OriginalLambdaBody(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement(translationArrayPtr, entityIndex), ref tag1, in tag2); entityIndex++; @@ -74,57 +74,54 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int batchInd if ((mask64 & 1) != 0) { global::TagComponent1 tag1 = default; - ; global::TagComponent2 tag2 = default; OriginalLambdaBody(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement(translationArrayPtr, entityIndex), ref tag1, in tag2); } - mask64 >>= 1; } - mask64 = chunkEnabledMask.ULong1; for (var entityIndex = 64; entityIndex < chunkEntityCount; ++entityIndex) { if ((mask64 & 1) != 0) { global::TagComponent1 tag1 = default; - ; global::TagComponent2 tag2 = default; OriginalLambdaBody(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetRefToNativeArrayPtrElement(translationArrayPtr, entityIndex), ref tag1, in tag2); } - mask64 >>= 1; } } } } - [global::Unity.Burst.BurstCompile] [global::AOT.MonoPInvokeCallback(typeof(global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkRunWithoutJobSystemDelegate))] public static void RunWithoutJobSystem(ref global::Unity.Entities.EntityQuery query, global::System.IntPtr jobPtr) { try { - global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.RunWithoutJobsInternal(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeAsRef(jobPtr), ref query); + global::Unity.Entities.Internal.InternalCompilerInterface.JobChunkInterface.RunWithoutJobsInternal(ref global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeAsRef(jobPtr), ref query); } finally { } } } - - void EntitiesForEachNonCapturing_625D99F_LambdaJob_0_Execute() + void EntitiesForEachNonCapturing_4E2AFFBE_LambdaJob_0_Execute() { __TypeHandle.__Translation_RW_ComponentTypeHandle.Update(ref this.CheckedStateRef); - var __job = new EntitiesForEachNonCapturing_625D99F_LambdaJob_0_Job{__translationTypeHandle = __TypeHandle.__Translation_RW_ComponentTypeHandle}; - if (!__query_1641826535_0.IsEmptyIgnoreFilter) + var __job = new EntitiesForEachNonCapturing_4E2AFFBE_LambdaJob_0_Job + { + __translationTypeHandle = __TypeHandle.__Translation_RW_ComponentTypeHandle + }; + + if(!__query_1641826535_0.IsEmptyIgnoreFilter) { this.CheckedStateRef.CompleteDependency(); - var __functionPointer = global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobCompilerEnabled ? EntitiesForEachNonCapturing_625D99F_LambdaJob_0_Job.FunctionPtrFieldBurst : EntitiesForEachNonCapturing_625D99F_LambdaJob_0_Job.FunctionPtrFieldNoBurst; + var __functionPointer = global::Unity.Jobs.LowLevel.Unsafe.JobsUtility.JobCompilerEnabled ? EntitiesForEachNonCapturing_4E2AFFBE_LambdaJob_0_Job.FunctionPtrFieldBurst : EntitiesForEachNonCapturing_4E2AFFBE_LambdaJob_0_Job.FunctionPtrFieldNoBurst; global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeRunJobChunk(ref __job, __query_1641826535_0, __functionPointer); } } - + TypeHandle __TypeHandle; global::Unity.Entities.EntityQuery __query_1641826535_0; struct TypeHandle @@ -135,20 +132,28 @@ public void __AssignHandles(ref global::Unity.Entities.SystemState state) { __Translation_RW_ComponentTypeHandle = state.GetComponentTypeHandle(false); } + } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void __AssignQueries(ref global::Unity.Entities.SystemState state) { - __query_1641826535_0 = state.GetEntityQuery(new global::Unity.Entities.EntityQueryDesc{All = new global::Unity.Entities.ComponentType[]{global::Unity.Entities.ComponentType.ReadOnly(), global::Unity.Entities.ComponentType.ReadOnly(), global::Unity.Entities.ComponentType.ReadWrite()}, Any = new global::Unity.Entities.ComponentType[]{}, None = new global::Unity.Entities.ComponentType[]{}, Disabled = new global::Unity.Entities.ComponentType[]{}, Absent = new global::Unity.Entities.ComponentType[]{}, Options = global::Unity.Entities.EntityQueryOptions.Default}); + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + __query_1641826535_0 = + entityQueryBuilder + .WithAll() + .WithAll() + .WithAllRW() + .Build(ref state); + entityQueryBuilder.Reset(); + entityQueryBuilder.Dispose(); } - + protected override void OnCreateForCompiler() { base.OnCreateForCompiler(); __AssignQueries(ref this.CheckedStateRef); __TypeHandle.__AssignHandles(ref this.CheckedStateRef); - EntitiesForEachNonCapturing_625D99F_LambdaJob_0_Job.FunctionPtrFieldNoBurst = EntitiesForEachNonCapturing_625D99F_LambdaJob_0_Job.RunWithoutJobSystem; - EntitiesForEachNonCapturing_625D99F_LambdaJob_0_Job.FunctionPtrFieldBurst = Unity.Entities.Internal.InternalCompilerInterface.BurstCompile(EntitiesForEachNonCapturing_625D99F_LambdaJob_0_Job.FunctionPtrFieldNoBurst); + EntitiesForEachNonCapturing_4E2AFFBE_LambdaJob_0_Job.FunctionPtrFieldNoBurst = EntitiesForEachNonCapturing_4E2AFFBE_LambdaJob_0_Job.RunWithoutJobSystem; + EntitiesForEachNonCapturing_4E2AFFBE_LambdaJob_0_Job.FunctionPtrFieldBurst = Unity.Entities.Internal.InternalCompilerInterface.BurstCompile(EntitiesForEachNonCapturing_4E2AFFBE_LambdaJob_0_Job.FunctionPtrFieldNoBurst); } -} \ No newline at end of file +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/JobEntityBasic/Test0__JobEntity_19875963023.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/JobEntityBasic/Test0__JobEntity_19875963023.g.cs index 21e3e07..7173595 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/JobEntityBasic/Test0__JobEntity_19875963023.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/JobEntityBasic/Test0__JobEntity_19875963023.g.cs @@ -1,5 +1,5 @@ #pragma warning disable 0219 -#line 1 "Temp/GeneratedCode/TestProject/Test0__JobEntity_19875963020.g.cs" +#line 1 "Temp/GeneratedCode/TestProject/Test0__JobEntity_19875963023.g.cs" using Unity.Entities; @@ -7,39 +7,33 @@ partial struct MyJob : global::Unity.Entities.IJobChunk { InternalCompilerQueryAndHandleData.TypeHandle __TypeHandle; - - - [global::System.Runtime.CompilerServices.CompilerGenerated] public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkIndexInQuery, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask) { - - int chunkEntityCount = chunk.Count; int matchingEntityCount = 0; + if (!useEnabledMask) { for(int entityIndexInChunk = 0; entityIndexInChunk < chunkEntityCount; ++entityIndexInChunk) { - Execute(); matchingEntityCount++; } } else { - int edgeCount = global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong0 ^ (chunkEnabledMask.ULong0 << 1)) + - global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong1 ^ (chunkEnabledMask.ULong1 << 1)) - 1; + int edgeCount = global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong0 ^ (chunkEnabledMask.ULong0 << 1)) + global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong1 ^ (chunkEnabledMask.ULong1 << 1)) - 1; bool useRanges = edgeCount <= 4; if (useRanges) { int entityIndexInChunk = 0; int chunkEndIndex = 0; - while (global::Unity.Entities.EnabledBitUtility.TryGetNextRange(chunkEnabledMask, chunkEndIndex, out entityIndexInChunk, out chunkEndIndex)) + + while (global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeTryGetNextEnabledBitRange(chunkEnabledMask, chunkEndIndex, out entityIndexInChunk, out chunkEndIndex)) { while (entityIndexInChunk < chunkEndIndex) { - Execute(); entityIndexInChunk++; matchingEntityCount++; @@ -54,7 +48,6 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkInd { if ((mask64 & 1) != 0) { - Execute(); matchingEntityCount++; } @@ -65,7 +58,6 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkInd { if ((mask64 & 1) != 0) { - Execute(); matchingEntityCount++; } @@ -73,16 +65,14 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkInd } } } - } global::Unity.Jobs.JobHandle __ThrowCodeGenException() => throw new global::System.Exception("This method should have been replaced by source gen."); - + // Emitted to disambiguate scheduling method invocations public void Run() => __ThrowCodeGenException(); public void RunByRef() => __ThrowCodeGenException(); public void Run(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); public void RunByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle Schedule(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); public global::Unity.Jobs.JobHandle ScheduleByRef(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); public global::Unity.Jobs.JobHandle Schedule(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); @@ -91,7 +81,6 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkInd public void ScheduleByRef() => __ThrowCodeGenException(); public void Schedule(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); public void ScheduleByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle ScheduleParallel(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); public global::Unity.Jobs.JobHandle ScheduleParallelByRef(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); public global::Unity.Jobs.JobHandle ScheduleParallel(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); @@ -102,114 +91,89 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkInd public void ScheduleParallelByRef() => __ThrowCodeGenException(); public void ScheduleParallel(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); public void ScheduleParallelByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - /// Used internally by the compiler, we won't promise this exists in the future -public struct InternalCompilerQueryAndHandleData -{ - public TypeHandle __TypeHandle; - public global::Unity.Entities.EntityQuery DefaultQuery; - - - public struct TypeHandle + public struct InternalCompilerQueryAndHandleData { - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void __AssignHandles(ref global::Unity.Entities.SystemState state) + public TypeHandle __TypeHandle; + public global::Unity.Entities.EntityQuery DefaultQuery; + public struct TypeHandle { + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public void __AssignHandles(ref global::Unity.Entities.SystemState state) + { + } + public void Update(ref global::Unity.Entities.SystemState state) + { + } + + } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + void __AssignQueries(ref global::Unity.Entities.SystemState state) + { + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + DefaultQuery = + entityQueryBuilder + .Build(ref state); + entityQueryBuilder.Reset(); + entityQueryBuilder.Dispose(); } - public void Update(ref global::Unity.Entities.SystemState state) { - } - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - void __AssignQueries(ref global::Unity.Entities.SystemState state) - { - DefaultQuery = state.GetEntityQuery - ( - new global::Unity.Entities.EntityQueryDesc - { - All = new global::Unity.Entities.ComponentType[]{}, - Any = new global::Unity.Entities.ComponentType[] { - - }, - None = new global::Unity.Entities.ComponentType[] { - - }, - Disabled = new global::Unity.Entities.ComponentType[] { - - }, - Absent = new global::Unity.Entities.ComponentType[] { - - }, - Options = - global::Unity.Entities.EntityQueryOptions.Default - } - ); + public void Init(ref global::Unity.Entities.SystemState state, bool assignDefaultQuery) + { + if (assignDefaultQuery) + __AssignQueries(ref state); + __TypeHandle.__AssignHandles(ref state); + } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public void Run(ref global::MyJob job, global::Unity.Entities.EntityQuery query) + { + job.__TypeHandle = __TypeHandle; + global::Unity.Entities.JobChunkExtensions.RunByRef(ref job, query); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public global::Unity.Jobs.JobHandle Schedule(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency) + { + job.__TypeHandle = __TypeHandle; + return global::Unity.Entities.JobChunkExtensions.ScheduleByRef(ref job, query, dependency); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public global::Unity.Jobs.JobHandle ScheduleParallel(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency) + { + job.__TypeHandle = __TypeHandle; + return global::Unity.Entities.JobChunkExtensions.ScheduleParallelByRef(ref job, query, dependency); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public void UpdateBaseEntityIndexArray(ref global::MyJob job, global::Unity.Entities.EntityQuery query, ref global::Unity.Entities.SystemState state) + { + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public global::Unity.Jobs.JobHandle UpdateBaseEntityIndexArray(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency, ref global::Unity.Entities.SystemState state) + { + return dependency; + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public void AssignEntityManager(ref global::MyJob job, global::Unity.Entities.EntityManager entityManager) + { + } } - - - public void Init(ref global::Unity.Entities.SystemState state, bool assignDefaultQuery) - { - if (assignDefaultQuery) { - __AssignQueries(ref state); - } - __TypeHandle.__AssignHandles(ref state); - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void Run(ref global::MyJob job, global::Unity.Entities.EntityQuery query) - { - job.__TypeHandle = __TypeHandle; - global::Unity.Entities.JobChunkExtensions.RunByRef(ref job, query); - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public global::Unity.Jobs.JobHandle Schedule(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency) - { - job.__TypeHandle = __TypeHandle; - return global::Unity.Entities.JobChunkExtensions.ScheduleByRef(ref job, query, dependency); - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public global::Unity.Jobs.JobHandle ScheduleParallel(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency) - { - job.__TypeHandle = __TypeHandle; - return global::Unity.Entities.JobChunkExtensions.ScheduleParallelByRef(ref job, query, dependency); - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void UpdateBaseEntityIndexArray(ref global::MyJob job, global::Unity.Entities.EntityQuery query, ref global::Unity.Entities.SystemState state) - { - - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public global::Unity.Jobs.JobHandle UpdateBaseEntityIndexArray(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency, ref global::Unity.Entities.SystemState state) - { - return dependency; - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void AssignEntityManager(ref global::MyJob job, global::Unity.Entities.EntityManager entityManager) - { - - } - -} - - /// Internal structure used by the compiler + /// Internal structure used by the compiler public struct InternalCompiler { [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] [global::System.Diagnostics.Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] // scheduleType 0:Run, 1:Schedule, 2:ScheduleParallel - public static void CheckForErrors(int scheduleType) { - + public static void CheckForErrors(int scheduleType) + { } } } + diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/JobEntityIfDirective/Test0__JobEntity_19875963024.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/JobEntityIfDirective/Test0__JobEntity_19875963024.g.cs index 21e3e07..700a571 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/JobEntityIfDirective/Test0__JobEntity_19875963024.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/JobEntityIfDirective/Test0__JobEntity_19875963024.g.cs @@ -1,5 +1,5 @@ #pragma warning disable 0219 -#line 1 "Temp/GeneratedCode/TestProject/Test0__JobEntity_19875963020.g.cs" +#line 1 "Temp/GeneratedCode/TestProject/Test0__JobEntity_19875963024.g.cs" using Unity.Entities; @@ -7,39 +7,33 @@ partial struct MyJob : global::Unity.Entities.IJobChunk { InternalCompilerQueryAndHandleData.TypeHandle __TypeHandle; - - - [global::System.Runtime.CompilerServices.CompilerGenerated] public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkIndexInQuery, bool useEnabledMask, in global::Unity.Burst.Intrinsics.v128 chunkEnabledMask) { - - int chunkEntityCount = chunk.Count; int matchingEntityCount = 0; + if (!useEnabledMask) { for(int entityIndexInChunk = 0; entityIndexInChunk < chunkEntityCount; ++entityIndexInChunk) { - Execute(); matchingEntityCount++; } } else { - int edgeCount = global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong0 ^ (chunkEnabledMask.ULong0 << 1)) + - global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong1 ^ (chunkEnabledMask.ULong1 << 1)) - 1; + int edgeCount = global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong0 ^ (chunkEnabledMask.ULong0 << 1)) + global::Unity.Mathematics.math.countbits(chunkEnabledMask.ULong1 ^ (chunkEnabledMask.ULong1 << 1)) - 1; bool useRanges = edgeCount <= 4; if (useRanges) { int entityIndexInChunk = 0; int chunkEndIndex = 0; - while (global::Unity.Entities.EnabledBitUtility.TryGetNextRange(chunkEnabledMask, chunkEndIndex, out entityIndexInChunk, out chunkEndIndex)) + + while (global::Unity.Entities.Internal.InternalCompilerInterface.UnsafeTryGetNextEnabledBitRange(chunkEnabledMask, chunkEndIndex, out entityIndexInChunk, out chunkEndIndex)) { while (entityIndexInChunk < chunkEndIndex) { - Execute(); entityIndexInChunk++; matchingEntityCount++; @@ -54,7 +48,6 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkInd { if ((mask64 & 1) != 0) { - Execute(); matchingEntityCount++; } @@ -65,7 +58,6 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkInd { if ((mask64 & 1) != 0) { - Execute(); matchingEntityCount++; } @@ -73,16 +65,14 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkInd } } } - } global::Unity.Jobs.JobHandle __ThrowCodeGenException() => throw new global::System.Exception("This method should have been replaced by source gen."); - + // Emitted to disambiguate scheduling method invocations public void Run() => __ThrowCodeGenException(); public void RunByRef() => __ThrowCodeGenException(); public void Run(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); public void RunByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle Schedule(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); public global::Unity.Jobs.JobHandle ScheduleByRef(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); public global::Unity.Jobs.JobHandle Schedule(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); @@ -91,7 +81,6 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkInd public void ScheduleByRef() => __ThrowCodeGenException(); public void Schedule(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); public void ScheduleByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - public global::Unity.Jobs.JobHandle ScheduleParallel(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); public global::Unity.Jobs.JobHandle ScheduleParallelByRef(global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); public global::Unity.Jobs.JobHandle ScheduleParallel(global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependsOn) => __ThrowCodeGenException(); @@ -102,114 +91,89 @@ public void Execute(in global::Unity.Entities.ArchetypeChunk chunk, int chunkInd public void ScheduleParallelByRef() => __ThrowCodeGenException(); public void ScheduleParallel(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); public void ScheduleParallelByRef(global::Unity.Entities.EntityQuery query) => __ThrowCodeGenException(); - /// Used internally by the compiler, we won't promise this exists in the future -public struct InternalCompilerQueryAndHandleData -{ - public TypeHandle __TypeHandle; - public global::Unity.Entities.EntityQuery DefaultQuery; - - - public struct TypeHandle + public struct InternalCompilerQueryAndHandleData { - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void __AssignHandles(ref global::Unity.Entities.SystemState state) + public TypeHandle __TypeHandle; + public global::Unity.Entities.EntityQuery DefaultQuery; + public struct TypeHandle { + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public void __AssignHandles(ref global::Unity.Entities.SystemState state) + { + } + public void Update(ref global::Unity.Entities.SystemState state) + { + } + + } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + void __AssignQueries(ref global::Unity.Entities.SystemState state) + { + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + DefaultQuery = + entityQueryBuilder + .Build(ref state); + entityQueryBuilder.Reset(); + entityQueryBuilder.Dispose(); } - public void Update(ref global::Unity.Entities.SystemState state) { - } - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - void __AssignQueries(ref global::Unity.Entities.SystemState state) - { - DefaultQuery = state.GetEntityQuery - ( - new global::Unity.Entities.EntityQueryDesc - { - All = new global::Unity.Entities.ComponentType[]{}, - Any = new global::Unity.Entities.ComponentType[] { - - }, - None = new global::Unity.Entities.ComponentType[] { - - }, - Disabled = new global::Unity.Entities.ComponentType[] { - - }, - Absent = new global::Unity.Entities.ComponentType[] { - - }, - Options = - global::Unity.Entities.EntityQueryOptions.Default - } - ); + public void Init(ref global::Unity.Entities.SystemState state, bool assignDefaultQuery) + { + if (assignDefaultQuery) + __AssignQueries(ref state); + __TypeHandle.__AssignHandles(ref state); + } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public void Run(ref global::MyJob job, global::Unity.Entities.EntityQuery query) + { + job.__TypeHandle = __TypeHandle; + global::Unity.Entities.JobChunkExtensions.RunByRef(ref job, query); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public global::Unity.Jobs.JobHandle Schedule(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency) + { + job.__TypeHandle = __TypeHandle; + return global::Unity.Entities.JobChunkExtensions.ScheduleByRef(ref job, query, dependency); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public global::Unity.Jobs.JobHandle ScheduleParallel(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency) + { + job.__TypeHandle = __TypeHandle; + return global::Unity.Entities.JobChunkExtensions.ScheduleParallelByRef(ref job, query, dependency); + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public void UpdateBaseEntityIndexArray(ref global::MyJob job, global::Unity.Entities.EntityQuery query, ref global::Unity.Entities.SystemState state) + { + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public global::Unity.Jobs.JobHandle UpdateBaseEntityIndexArray(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency, ref global::Unity.Entities.SystemState state) + { + return dependency; + } + + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public void AssignEntityManager(ref global::MyJob job, global::Unity.Entities.EntityManager entityManager) + { + } } - - - public void Init(ref global::Unity.Entities.SystemState state, bool assignDefaultQuery) - { - if (assignDefaultQuery) { - __AssignQueries(ref state); - } - __TypeHandle.__AssignHandles(ref state); - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void Run(ref global::MyJob job, global::Unity.Entities.EntityQuery query) - { - job.__TypeHandle = __TypeHandle; - global::Unity.Entities.JobChunkExtensions.RunByRef(ref job, query); - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public global::Unity.Jobs.JobHandle Schedule(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency) - { - job.__TypeHandle = __TypeHandle; - return global::Unity.Entities.JobChunkExtensions.ScheduleByRef(ref job, query, dependency); - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public global::Unity.Jobs.JobHandle ScheduleParallel(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency) - { - job.__TypeHandle = __TypeHandle; - return global::Unity.Entities.JobChunkExtensions.ScheduleParallelByRef(ref job, query, dependency); - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void UpdateBaseEntityIndexArray(ref global::MyJob job, global::Unity.Entities.EntityQuery query, ref global::Unity.Entities.SystemState state) - { - - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public global::Unity.Jobs.JobHandle UpdateBaseEntityIndexArray(ref global::MyJob job, global::Unity.Entities.EntityQuery query, global::Unity.Jobs.JobHandle dependency, ref global::Unity.Entities.SystemState state) - { - return dependency; - } - - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public void AssignEntityManager(ref global::MyJob job, global::Unity.Entities.EntityManager entityManager) - { - - } - -} - - /// Internal structure used by the compiler + /// Internal structure used by the compiler public struct InternalCompiler { [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] [global::System.Diagnostics.Conditional("ENABLE_UNITY_COLLECTIONS_CHECKS")] // scheduleType 0:Run, 1:Schedule, 2:ScheduleParallel - public static void CheckForErrors(int scheduleType) { - + public static void CheckForErrors(int scheduleType) + { } } } + diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/NestedSystemAPIInvocation/Test0__System_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/NestedSystemAPIInvocation/Test0__System_19875963020.g.cs new file mode 100644 index 0000000..58799be --- /dev/null +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/NestedSystemAPIInvocation/Test0__System_19875963020.g.cs @@ -0,0 +1,43 @@ +#pragma warning disable 0219 +#line 1 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" +using Unity.Burst; +using Unity.Entities; +using Unity.Entities.Tests; +[global::System.Runtime.CompilerServices.CompilerGenerated] +public unsafe partial struct PartialMethodSystem : global::Unity.Entities.ISystemCompilerGenerated +{ + [global::Unity.Entities.DOTSCompilerPatchedMethod("ToggleEnabled_T0_Unity.Entities.Entity_ref_Unity.Entities.SystemState&")] + + void __ToggleEnabled_5DBCC748(Entity entity, ref SystemState state) + { + #line 15 "/0/Test0.cs" +global::Unity.Entities.Internal.InternalCompilerInterface.SetComponentEnabledAfterCompletingDependency(ref __TypeHandle.__Unity_Entities_Tests_EcsTestDataEnableable_RW_ComponentLookup, ref state, entity, !global::Unity.Entities.Internal.InternalCompilerInterface.IsComponentEnabledAfterCompletingDependency(ref __TypeHandle.__Unity_Entities_Tests_EcsTestDataEnableable_RO_ComponentLookup, ref state, entity)); + } + + + TypeHandle __TypeHandle; + struct TypeHandle + { + public Unity.Entities.ComponentLookup __Unity_Entities_Tests_EcsTestDataEnableable_RW_ComponentLookup; + [global::Unity.Collections.ReadOnly] public Unity.Entities.ComponentLookup __Unity_Entities_Tests_EcsTestDataEnableable_RO_ComponentLookup; + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + public void __AssignHandles(ref global::Unity.Entities.SystemState state) + { + __Unity_Entities_Tests_EcsTestDataEnableable_RW_ComponentLookup = state.GetComponentLookup(false); + __Unity_Entities_Tests_EcsTestDataEnableable_RO_ComponentLookup = state.GetComponentLookup(true); + } + + } + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + void __AssignQueries(ref global::Unity.Entities.SystemState state) + { + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + entityQueryBuilder.Dispose(); + } + + public void OnCreateForCompiler(ref SystemState state) + { + __AssignQueries(ref state); + __TypeHandle.__AssignHandles(ref state); + } +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemAPIInPartialMethod/Test0__System_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemAPIInPartialMethod/Test0__System_19875963020.g.cs index 3df940a..dad395f 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemAPIInPartialMethod/Test0__System_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemAPIInPartialMethod/Test0__System_19875963020.g.cs @@ -3,17 +3,17 @@ using Unity.Burst; using Unity.Entities; using Unity.Entities.Tests; - [global::System.Runtime.CompilerServices.CompilerGenerated] -public unsafe partial struct PartialMethodSystem : global::Unity.Entities.ISystem, global::Unity.Entities.ISystemCompilerGenerated +public unsafe partial struct PartialMethodSystem : global::Unity.Entities.ISystemCompilerGenerated { - [global::Unity.Entities.DOTSCompilerPatchedMethod("CustomOnUpdate_ref_Unity.Entities.SystemState")] - void __CustomOnUpdate_462BE639(ref SystemState state) + [global::Unity.Entities.DOTSCompilerPatchedMethod("CustomOnUpdate_T0_ref_Unity.Entities.SystemState&")] + void __CustomOnUpdate_5D2F01C4(ref SystemState state) { #line 10 "/0/Test0.cs" var tickSingleton2 = __query_1641826531_0.GetSingleton(); } + TypeHandle __TypeHandle; global::Unity.Entities.EntityQuery __query_1641826531_0; struct TypeHandle @@ -22,17 +22,24 @@ struct TypeHandle public void __AssignHandles(ref global::Unity.Entities.SystemState state) { } + } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void __AssignQueries(ref global::Unity.Entities.SystemState state) { - __query_1641826531_0 = state.GetEntityQuery(new global::Unity.Entities.EntityQueryDesc{All = new global::Unity.Entities.ComponentType[]{global::Unity.Entities.ComponentType.ReadOnly()}, Any = new global::Unity.Entities.ComponentType[]{}, None = new global::Unity.Entities.ComponentType[]{}, Disabled = new global::Unity.Entities.ComponentType[]{}, Absent = new global::Unity.Entities.ComponentType[]{}, Options = global::Unity.Entities.EntityQueryOptions.Default | global::Unity.Entities.EntityQueryOptions.IncludeSystems}); + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + __query_1641826531_0 = + entityQueryBuilder + .WithAll() + .WithOptions(global::Unity.Entities.EntityQueryOptions.IncludeSystems) + .Build(ref state); + entityQueryBuilder.Reset(); + entityQueryBuilder.Dispose(); } - + public void OnCreateForCompiler(ref SystemState state) { __AssignQueries(ref state); __TypeHandle.__AssignHandles(ref state); } -} \ No newline at end of file +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithAspectInvocation/Test0__System_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithAspectInvocation/Test0__System_19875963020.g.cs index c3a7a96..0de151d 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithAspectInvocation/Test0__System_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithAspectInvocation/Test0__System_19875963020.g.cs @@ -4,23 +4,19 @@ using Unity.Entities; using Unity.Entities.Tests; using static Unity.Entities.SystemAPI; - [global::System.Runtime.CompilerServices.CompilerGenerated] -public partial struct RotationSpeedSystemForEachISystem : global::Unity.Entities.ISystem, global::Unity.Entities.ISystemCompilerGenerated +public partial struct RotationSpeedSystemForEachISystem : global::Unity.Entities.ISystemCompilerGenerated { - [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_ref_Unity.Entities.SystemState")] - void __OnUpdate_6E994214(ref SystemState state) + [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_T0_ref_Unity.Entities.SystemState&")] + void __OnUpdate_6D4E9467(ref SystemState state) { #line 16 "/0/Test0.cs" Entity entity = default; - #line 17 "/0/Test0.cs" - global::Unity.Entities.Tests.EcsTestAspect.CompleteDependencyBeforeRW(ref state); - #line hidden - __TypeHandle.__Unity_Entities_Tests_EcsTestAspect_RW_AspectLookup.Update(ref state); - #line hidden - var testAspectRO = __TypeHandle.__Unity_Entities_Tests_EcsTestAspect_RW_AspectLookup[entity]; + #line 17 "/0/Test0.cs" + var testAspectRO = global::Unity.Entities.Internal.InternalCompilerInterface.GetAspectAfterCompletingDependency(ref __TypeHandle.__Unity_Entities_Tests_EcsTestAspect_RW_AspectLookup, ref state, false, entity); } + TypeHandle __TypeHandle; struct TypeHandle { @@ -30,16 +26,18 @@ public void __AssignHandles(ref global::Unity.Entities.SystemState state) { __Unity_Entities_Tests_EcsTestAspect_RW_AspectLookup = new global::Unity.Entities.Tests.EcsTestAspect.Lookup(ref state); } + } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void __AssignQueries(ref global::Unity.Entities.SystemState state) { + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + entityQueryBuilder.Dispose(); } - + public void OnCreateForCompiler(ref SystemState state) { __AssignQueries(ref state); __TypeHandle.__AssignHandles(ref state); } -} \ No newline at end of file +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithBufferAccessInvocation/Test0__System_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithBufferAccessInvocation/Test0__System_19875963020.g.cs index f7df781..1043592 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithBufferAccessInvocation/Test0__System_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithBufferAccessInvocation/Test0__System_19875963020.g.cs @@ -4,51 +4,32 @@ using Unity.Entities; using Unity.Entities.Tests; using static Unity.Entities.SystemAPI; - [global::System.Runtime.CompilerServices.CompilerGenerated] -public partial struct RotationSpeedSystemForEachISystem : global::Unity.Entities.ISystem, global::Unity.Entities.ISystemCompilerGenerated +public partial struct RotationSpeedSystemForEachISystem : global::Unity.Entities.ISystemCompilerGenerated { - [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_ref_Unity.Entities.SystemState")] - void __OnUpdate_6E994214(ref SystemState state) + [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_T0_ref_Unity.Entities.SystemState&")] + void __OnUpdate_6D4E9467(ref SystemState state) { - #line 21 "/0/Test0.cs" - if (!__query_1641826537_0.IsEmptyIgnoreFilter) + #line 21 "/0/Test0.cs" + foreach (var (data, entity) in IFE_1641826537_0.Query(__query_1641826537_0, __TypeHandle.__IFE_1641826537_0_RW_TypeHandle, ref state)) + #line 22 "/0/Test0.cs" + { + #line 23 "/0/Test0.cs" + var lookup_rw = global::Unity.Entities.Internal.InternalCompilerInterface.GetBufferLookup(ref __TypeHandle.__BufferData_RW_BufferLookup, ref state); + #line 24 "/0/Test0.cs" + var lookup_ro = global::Unity.Entities.Internal.InternalCompilerInterface.GetBufferLookup(ref __TypeHandle.__BufferData_RO_BufferLookup, ref state); + #line 26 "/0/Test0.cs" + + if (global::Unity.Entities.Internal.InternalCompilerInterface.HasBufferAfterCompletingDependency(ref __TypeHandle.__BufferData_RO_BufferLookup, ref state, entity)) + #line 27 "/0/Test0.cs" { - IFE_1641826537_0.CompleteDependencyBeforeRW(ref state); - #line hidden - __TypeHandle.__IFE_1641826537_0_RW_TypeHandle.Update(ref state); - #line hidden - foreach (var(data, entity)in IFE_1641826537_0.Query(__query_1641826537_0, __TypeHandle.__IFE_1641826537_0_RW_TypeHandle)) - { - #line 23 "/0/Test0.cs" - __TypeHandle.__BufferData_RW_BufferLookup.Update(ref state); - #line hidden - var lookup_rw = __TypeHandle.__BufferData_RW_BufferLookup; - #line 24 "/0/Test0.cs" - __TypeHandle.__BufferData_RO_BufferLookup.Update(ref state); - #line hidden - var lookup_ro = __TypeHandle.__BufferData_RO_BufferLookup; - #line 28 "/0/Test0.cs" - state.EntityManager.CompleteDependencyBeforeRO(); - #line hidden - __TypeHandle.__BufferData_RO_BufferLookup.Update(ref state); - #line hidden - if (__TypeHandle.__BufferData_RO_BufferLookup.HasBuffer(entity)) - { - #line 28 "/0/Test0.cs" - state.EntityManager.CompleteDependencyBeforeRW(); - #line hidden - __TypeHandle.__BufferData_RW_BufferLookup.Update(ref state); - #line hidden - var rotation = __TypeHandle.__BufferData_RW_BufferLookup[entity]; - } - } - #line hidden - - } + #line 28 "/0/Test0.cs" + var rotation = global::Unity.Entities.Internal.InternalCompilerInterface.GetBufferAfterCompletingDependency(ref __TypeHandle.__BufferData_RW_BufferLookup, ref state, entity); + } + } } - #line 52 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" + #line 33 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" readonly struct IFE_1641826537_0 { public struct ResolvedChunk @@ -56,29 +37,22 @@ public struct ResolvedChunk public global::System.IntPtr item1_IntPtr; public global::System.IntPtr Entity_IntPtr; [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public Unity.Entities.QueryEnumerableWithEntity> Get(int index) - { - return new Unity.Entities.QueryEnumerableWithEntity>(Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRO(item1_IntPtr, index), Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement(Entity_IntPtr, index)); - } + public Unity.Entities.QueryEnumerableWithEntity> Get(int index) => new Unity.Entities.QueryEnumerableWithEntity>(Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRO(item1_IntPtr, index),Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement(Entity_IntPtr, index)); } - public struct TypeHandle { - [Unity.Collections.ReadOnly] - Unity.Entities.ComponentTypeHandle item1_ComponentTypeHandle_RO; + [Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle item1_ComponentTypeHandle_RO; Unity.Entities.EntityTypeHandle Entity_TypeHandle; public TypeHandle(ref global::Unity.Entities.SystemState systemState, bool isReadOnly) { item1_ComponentTypeHandle_RO = systemState.GetComponentTypeHandle(isReadOnly: true); Entity_TypeHandle = systemState.GetEntityTypeHandle(); } - public void Update(ref global::Unity.Entities.SystemState systemState) { item1_ComponentTypeHandle_RO.Update(ref systemState); Entity_TypeHandle.Update(ref systemState); } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public ResolvedChunk Resolve(global::Unity.Entities.ArchetypeChunk archetypeChunk) { @@ -88,67 +62,79 @@ public ResolvedChunk Resolve(global::Unity.Entities.ArchetypeChunk archetypeChun return resolvedChunk; } } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static Enumerator Query(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle) => new Enumerator(entityQuery, typeHandle); + public static Enumerator Query(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle, ref Unity.Entities.SystemState state) => new Enumerator(entityQuery, typeHandle, ref state); public struct Enumerator : global::System.Collections.Generic.IEnumerator>> { global::Unity.Entities.Internal.InternalEntityQueryEnumerator _entityQueryEnumerator; TypeHandle _typeHandle; ResolvedChunk _resolvedChunk; + int _currentEntityIndex; int _endEntityIndex; - public Enumerator(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle) + + public Enumerator(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle, ref Unity.Entities.SystemState state) { + if (!entityQuery.IsEmptyIgnoreFilter) + { + IFE_1641826537_0.CompleteDependencyBeforeRW(ref state); + typeHandle.Update(ref state); + + } + _entityQueryEnumerator = new global::Unity.Entities.Internal.InternalEntityQueryEnumerator(entityQuery); + _currentEntityIndex = -1; _endEntityIndex = -1; + _typeHandle = typeHandle; _resolvedChunk = default; } - + public void Dispose() => _entityQueryEnumerator.Dispose(); + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public bool MoveNext() { _currentEntityIndex++; + if (_currentEntityIndex >= _endEntityIndex) { if (_entityQueryEnumerator.MoveNextEntityRange(out bool movedToNewChunk, out global::Unity.Entities.ArchetypeChunk chunk, out int entityStartIndex, out int entityEndIndex)) { if (movedToNewChunk) + { _resolvedChunk = _typeHandle.Resolve(chunk); + } + _currentEntityIndex = entityStartIndex; _endEntityIndex = entityEndIndex; return true; } - return false; } - return true; } - + public Unity.Entities.QueryEnumerableWithEntity> Current => _resolvedChunk.Get(_currentEntityIndex); + public Enumerator GetEnumerator() => this; public void Reset() => throw new global::System.NotImplementedException(); object global::System.Collections.IEnumerator.Current => throw new global::System.NotImplementedException(); } - public static void CompleteDependencyBeforeRW(ref SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); } } - + TypeHandle __TypeHandle; global::Unity.Entities.EntityQuery __query_1641826537_0; struct TypeHandle { public IFE_1641826537_0.TypeHandle __IFE_1641826537_0_RW_TypeHandle; public Unity.Entities.BufferLookup __BufferData_RW_BufferLookup; - [global::Unity.Collections.ReadOnly] - public Unity.Entities.BufferLookup __BufferData_RO_BufferLookup; + [global::Unity.Collections.ReadOnly] public Unity.Entities.BufferLookup __BufferData_RO_BufferLookup; [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public void __AssignHandles(ref global::Unity.Entities.SystemState state) { @@ -156,17 +142,23 @@ public void __AssignHandles(ref global::Unity.Entities.SystemState state) __BufferData_RW_BufferLookup = state.GetBufferLookup(false); __BufferData_RO_BufferLookup = state.GetBufferLookup(true); } + } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void __AssignQueries(ref global::Unity.Entities.SystemState state) { - __query_1641826537_0 = state.GetEntityQuery(new global::Unity.Entities.EntityQueryDesc{All = new global::Unity.Entities.ComponentType[]{global::Unity.Entities.ComponentType.ReadOnly()}, Any = new global::Unity.Entities.ComponentType[]{}, None = new global::Unity.Entities.ComponentType[]{}, Disabled = new global::Unity.Entities.ComponentType[]{}, Absent = new global::Unity.Entities.ComponentType[]{}, Options = global::Unity.Entities.EntityQueryOptions.Default}); + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + __query_1641826537_0 = + entityQueryBuilder + .WithAll() + .Build(ref state); + entityQueryBuilder.Reset(); + entityQueryBuilder.Dispose(); } - + public void OnCreateForCompiler(ref SystemState state) { __AssignQueries(ref state); __TypeHandle.__AssignHandles(ref state); } -} \ No newline at end of file +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithComponentAccessInvocation/Test0__System_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithComponentAccessInvocation/Test0__System_19875963020.g.cs index 5adad2b..a954c43 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithComponentAccessInvocation/Test0__System_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithComponentAccessInvocation/Test0__System_19875963020.g.cs @@ -4,35 +4,22 @@ using Unity.Entities; using Unity.Entities.Tests; using static Unity.Entities.SystemAPI; - [global::System.Runtime.CompilerServices.CompilerGenerated] -public partial struct RotationSpeedSystemForEachISystem : global::Unity.Entities.ISystem, global::Unity.Entities.ISystemCompilerGenerated +public partial struct RotationSpeedSystemForEachISystem : global::Unity.Entities.ISystemCompilerGenerated { - [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_ref_Unity.Entities.SystemState")] - void __OnUpdate_6E994214(ref SystemState state) + [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_T0_ref_Unity.Entities.SystemState&")] + void __OnUpdate_6D4E9467(ref SystemState state) { - #line 21 "/0/Test0.cs" - if (!__query_1641826537_0.IsEmptyIgnoreFilter) - { - IFE_1641826537_0.CompleteDependencyBeforeRW(ref state); - #line hidden - __TypeHandle.__IFE_1641826537_0_RW_TypeHandle.Update(ref state); - #line hidden - foreach (var(rotationSpeed, entity)in IFE_1641826537_0.Query(__query_1641826537_0, __TypeHandle.__IFE_1641826537_0_RW_TypeHandle)) - { - #line 23 "/0/Test0.cs" - state.EntityManager.CompleteDependencyBeforeRO(); - #line hidden - __TypeHandle.__Unity_Entities_Tests_EcsTestData_RO_ComponentLookup.Update(ref state); - #line hidden - var rotation = __TypeHandle.__Unity_Entities_Tests_EcsTestData_RO_ComponentLookup[entity]; - } - #line hidden - - } + #line 21 "/0/Test0.cs" + foreach (var (rotationSpeed, entity) in IFE_1641826537_0.Query(__query_1641826537_0, __TypeHandle.__IFE_1641826537_0_RW_TypeHandle, ref state)) + #line 22 "/0/Test0.cs" + { + #line 23 "/0/Test0.cs" + var rotation = global::Unity.Entities.Internal.InternalCompilerInterface.GetComponentAfterCompletingDependency(ref __TypeHandle.__Unity_Entities_Tests_EcsTestData_RO_ComponentLookup, ref state, entity); + } } - #line 36 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" + #line 23 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" readonly struct IFE_1641826537_0 { public struct ResolvedChunk @@ -40,29 +27,22 @@ public struct ResolvedChunk public global::System.IntPtr item1_IntPtr; public global::System.IntPtr Entity_IntPtr; [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public Unity.Entities.QueryEnumerableWithEntity> Get(int index) - { - return new Unity.Entities.QueryEnumerableWithEntity>(Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRO(item1_IntPtr, index), Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement(Entity_IntPtr, index)); - } + public Unity.Entities.QueryEnumerableWithEntity> Get(int index) => new Unity.Entities.QueryEnumerableWithEntity>(Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRO(item1_IntPtr, index),Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement(Entity_IntPtr, index)); } - public struct TypeHandle { - [Unity.Collections.ReadOnly] - Unity.Entities.ComponentTypeHandle item1_ComponentTypeHandle_RO; + [Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle item1_ComponentTypeHandle_RO; Unity.Entities.EntityTypeHandle Entity_TypeHandle; public TypeHandle(ref global::Unity.Entities.SystemState systemState, bool isReadOnly) { item1_ComponentTypeHandle_RO = systemState.GetComponentTypeHandle(isReadOnly: true); Entity_TypeHandle = systemState.GetEntityTypeHandle(); } - public void Update(ref global::Unity.Entities.SystemState systemState) { item1_ComponentTypeHandle_RO.Update(ref systemState); Entity_TypeHandle.Update(ref systemState); } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public ResolvedChunk Resolve(global::Unity.Entities.ArchetypeChunk archetypeChunk) { @@ -72,83 +52,101 @@ public ResolvedChunk Resolve(global::Unity.Entities.ArchetypeChunk archetypeChun return resolvedChunk; } } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static Enumerator Query(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle) => new Enumerator(entityQuery, typeHandle); + public static Enumerator Query(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle, ref Unity.Entities.SystemState state) => new Enumerator(entityQuery, typeHandle, ref state); public struct Enumerator : global::System.Collections.Generic.IEnumerator>> { global::Unity.Entities.Internal.InternalEntityQueryEnumerator _entityQueryEnumerator; TypeHandle _typeHandle; ResolvedChunk _resolvedChunk; + int _currentEntityIndex; int _endEntityIndex; - public Enumerator(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle) + + public Enumerator(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle, ref Unity.Entities.SystemState state) { + if (!entityQuery.IsEmptyIgnoreFilter) + { + IFE_1641826537_0.CompleteDependencyBeforeRW(ref state); + typeHandle.Update(ref state); + + } + _entityQueryEnumerator = new global::Unity.Entities.Internal.InternalEntityQueryEnumerator(entityQuery); + _currentEntityIndex = -1; _endEntityIndex = -1; + _typeHandle = typeHandle; _resolvedChunk = default; } - + public void Dispose() => _entityQueryEnumerator.Dispose(); + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public bool MoveNext() { _currentEntityIndex++; + if (_currentEntityIndex >= _endEntityIndex) { if (_entityQueryEnumerator.MoveNextEntityRange(out bool movedToNewChunk, out global::Unity.Entities.ArchetypeChunk chunk, out int entityStartIndex, out int entityEndIndex)) { if (movedToNewChunk) + { _resolvedChunk = _typeHandle.Resolve(chunk); + } + _currentEntityIndex = entityStartIndex; _endEntityIndex = entityEndIndex; return true; } - return false; } - return true; } - + public Unity.Entities.QueryEnumerableWithEntity> Current => _resolvedChunk.Get(_currentEntityIndex); + public Enumerator GetEnumerator() => this; public void Reset() => throw new global::System.NotImplementedException(); object global::System.Collections.IEnumerator.Current => throw new global::System.NotImplementedException(); } - public static void CompleteDependencyBeforeRW(ref SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); } } - + TypeHandle __TypeHandle; global::Unity.Entities.EntityQuery __query_1641826537_0; struct TypeHandle { public IFE_1641826537_0.TypeHandle __IFE_1641826537_0_RW_TypeHandle; - [global::Unity.Collections.ReadOnly] - public Unity.Entities.ComponentLookup __Unity_Entities_Tests_EcsTestData_RO_ComponentLookup; + [global::Unity.Collections.ReadOnly] public Unity.Entities.ComponentLookup __Unity_Entities_Tests_EcsTestData_RO_ComponentLookup; [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public void __AssignHandles(ref global::Unity.Entities.SystemState state) { __IFE_1641826537_0_RW_TypeHandle = new IFE_1641826537_0.TypeHandle(ref state, isReadOnly: false); __Unity_Entities_Tests_EcsTestData_RO_ComponentLookup = state.GetComponentLookup(true); } + } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void __AssignQueries(ref global::Unity.Entities.SystemState state) { - __query_1641826537_0 = state.GetEntityQuery(new global::Unity.Entities.EntityQueryDesc{All = new global::Unity.Entities.ComponentType[]{global::Unity.Entities.ComponentType.ReadOnly()}, Any = new global::Unity.Entities.ComponentType[]{}, None = new global::Unity.Entities.ComponentType[]{}, Disabled = new global::Unity.Entities.ComponentType[]{}, Absent = new global::Unity.Entities.ComponentType[]{}, Options = global::Unity.Entities.EntityQueryOptions.Default}); + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + __query_1641826537_0 = + entityQueryBuilder + .WithAll() + .Build(ref state); + entityQueryBuilder.Reset(); + entityQueryBuilder.Dispose(); } - + public void OnCreateForCompiler(ref SystemState state) { __AssignQueries(ref state); __TypeHandle.__AssignHandles(ref state); } -} \ No newline at end of file +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithManagedComponent/Test0__System_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithManagedComponent/Test0__System_19875963020.g.cs index 826f6a4..be13a2b 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithManagedComponent/Test0__System_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithManagedComponent/Test0__System_19875963020.g.cs @@ -4,13 +4,11 @@ using Unity.Entities; using Unity.Entities.Tests; using static Unity.Entities.SystemAPI; - [global::System.Runtime.CompilerServices.CompilerGenerated] -public partial struct SomeSystem : global::Unity.Entities.ISystem, global::Unity.Entities.ISystemCompilerGenerated +public partial struct SomeSystem : global::Unity.Entities.ISystemCompilerGenerated { - [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_ref_Unity.Entities.SystemState")] - void __OnUpdate_6E994214(ref SystemState state) - { + [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_T0_ref_Unity.Entities.SystemState&")] + void __OnUpdate_6D4E9467(ref SystemState state){ #line 10 "/0/Test0.cs" var e = state.EntityManager.CreateEntity(); #line 11 "/0/Test0.cs" @@ -19,6 +17,7 @@ void __OnUpdate_6E994214(ref SystemState state) var comp = __query_1641826531_0.GetSingleton().value; } + TypeHandle __TypeHandle; global::Unity.Entities.EntityQuery __query_1641826531_0; struct TypeHandle @@ -27,17 +26,24 @@ struct TypeHandle public void __AssignHandles(ref global::Unity.Entities.SystemState state) { } + } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void __AssignQueries(ref global::Unity.Entities.SystemState state) { - __query_1641826531_0 = state.GetEntityQuery(new global::Unity.Entities.EntityQueryDesc{All = new global::Unity.Entities.ComponentType[]{global::Unity.Entities.ComponentType.ReadOnly()}, Any = new global::Unity.Entities.ComponentType[]{}, None = new global::Unity.Entities.ComponentType[]{}, Disabled = new global::Unity.Entities.ComponentType[]{}, Absent = new global::Unity.Entities.ComponentType[]{}, Options = global::Unity.Entities.EntityQueryOptions.Default | global::Unity.Entities.EntityQueryOptions.IncludeSystems}); + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + __query_1641826531_0 = + entityQueryBuilder + .WithAll() + .WithOptions(global::Unity.Entities.EntityQueryOptions.IncludeSystems) + .Build(ref state); + entityQueryBuilder.Reset(); + entityQueryBuilder.Dispose(); } - + public void OnCreateForCompiler(ref SystemState state) { __AssignQueries(ref state); __TypeHandle.__AssignHandles(ref state); } -} \ No newline at end of file +} diff --git a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithStorageInfoInvocation/Test0__System_19875963020.g.cs b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithStorageInfoInvocation/Test0__System_19875963020.g.cs index a6daad9..3cc0be6 100644 --- a/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithStorageInfoInvocation/Test0__System_19875963020.g.cs +++ b/Unity.Entities/SourceGenerators/Source~/SystemGeneratorTests/verification-results/SystemMethodWithStorageInfoInvocation/Test0__System_19875963020.g.cs @@ -4,39 +4,26 @@ using Unity.Entities; using Unity.Entities.Tests; using static Unity.Entities.SystemAPI; - [global::System.Runtime.CompilerServices.CompilerGenerated] -public partial struct RotationSpeedSystemForEachISystem : global::Unity.Entities.ISystem, global::Unity.Entities.ISystemCompilerGenerated +public partial struct RotationSpeedSystemForEachISystem : global::Unity.Entities.ISystemCompilerGenerated { - [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_ref_Unity.Entities.SystemState")] - void __OnUpdate_6E994214(ref SystemState state) + [global::Unity.Entities.DOTSCompilerPatchedMethod("OnUpdate_T0_ref_Unity.Entities.SystemState&")] + void __OnUpdate_6D4E9467(ref SystemState state) { - #line 21 "/0/Test0.cs" - __TypeHandle.__EntityStorageInfoLookup.Update(ref state); - #line hidden - var storageInfo = __TypeHandle.__EntityStorageInfoLookup; - #line 22 "/0/Test0.cs" - if (!__query_1641826537_0.IsEmptyIgnoreFilter) - { - IFE_1641826537_0.CompleteDependencyBeforeRW(ref state); - #line hidden - __TypeHandle.__IFE_1641826537_0_RW_TypeHandle.Update(ref state); - #line hidden - foreach (var(data, entity)in IFE_1641826537_0.Query(__query_1641826537_0, __TypeHandle.__IFE_1641826537_0_RW_TypeHandle)) - { - #line 24 "/0/Test0.cs" - __TypeHandle.__EntityStorageInfoLookup.Update(ref state); - #line hidden - var check1 = __TypeHandle.__EntityStorageInfoLookup.Exists(entity); - #line 25 "/0/Test0.cs" - var check2 = storageInfo.Exists(entity); - } - #line hidden - - } + #line 21 "/0/Test0.cs" + var storageInfo = global::Unity.Entities.Internal.InternalCompilerInterface.GetEntityStorageInfoLookup(ref __TypeHandle.__EntityStorageInfoLookup, ref state); + #line 22 "/0/Test0.cs" + foreach (var (data, entity) in IFE_1641826537_0.Query(__query_1641826537_0, __TypeHandle.__IFE_1641826537_0_RW_TypeHandle, ref state)) + #line 23 "/0/Test0.cs" + { + #line 24 "/0/Test0.cs" + var check1 = global::Unity.Entities.Internal.InternalCompilerInterface.DoesEntityExist(ref __TypeHandle.__EntityStorageInfoLookup, ref state, entity); + #line 25 "/0/Test0.cs" + var check2 = storageInfo.Exists(entity); + } } - #line 40 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" + #line 27 "Temp/GeneratedCode/TestProject/Test0__System_19875963020.g.cs" readonly struct IFE_1641826537_0 { public struct ResolvedChunk @@ -44,29 +31,22 @@ public struct ResolvedChunk public global::System.IntPtr item1_IntPtr; public global::System.IntPtr Entity_IntPtr; [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public Unity.Entities.QueryEnumerableWithEntity> Get(int index) - { - return new Unity.Entities.QueryEnumerableWithEntity>(Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRO(item1_IntPtr, index), Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement(Entity_IntPtr, index)); - } + public Unity.Entities.QueryEnumerableWithEntity> Get(int index) => new Unity.Entities.QueryEnumerableWithEntity>(Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetUncheckedRefRO(item1_IntPtr, index),Unity.Entities.Internal.InternalCompilerInterface.UnsafeGetCopyOfNativeArrayPtrElement(Entity_IntPtr, index)); } - public struct TypeHandle { - [Unity.Collections.ReadOnly] - Unity.Entities.ComponentTypeHandle item1_ComponentTypeHandle_RO; + [Unity.Collections.ReadOnly] Unity.Entities.ComponentTypeHandle item1_ComponentTypeHandle_RO; Unity.Entities.EntityTypeHandle Entity_TypeHandle; public TypeHandle(ref global::Unity.Entities.SystemState systemState, bool isReadOnly) { item1_ComponentTypeHandle_RO = systemState.GetComponentTypeHandle(isReadOnly: true); Entity_TypeHandle = systemState.GetEntityTypeHandle(); } - public void Update(ref global::Unity.Entities.SystemState systemState) { item1_ComponentTypeHandle_RO.Update(ref systemState); Entity_TypeHandle.Update(ref systemState); } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public ResolvedChunk Resolve(global::Unity.Entities.ArchetypeChunk archetypeChunk) { @@ -76,83 +56,101 @@ public ResolvedChunk Resolve(global::Unity.Entities.ArchetypeChunk archetypeChun return resolvedChunk; } } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - public static Enumerator Query(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle) => new Enumerator(entityQuery, typeHandle); + public static Enumerator Query(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle, ref Unity.Entities.SystemState state) => new Enumerator(entityQuery, typeHandle, ref state); public struct Enumerator : global::System.Collections.Generic.IEnumerator>> { global::Unity.Entities.Internal.InternalEntityQueryEnumerator _entityQueryEnumerator; TypeHandle _typeHandle; ResolvedChunk _resolvedChunk; + int _currentEntityIndex; int _endEntityIndex; - public Enumerator(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle) + + public Enumerator(global::Unity.Entities.EntityQuery entityQuery, TypeHandle typeHandle, ref Unity.Entities.SystemState state) { + if (!entityQuery.IsEmptyIgnoreFilter) + { + IFE_1641826537_0.CompleteDependencyBeforeRW(ref state); + typeHandle.Update(ref state); + + } + _entityQueryEnumerator = new global::Unity.Entities.Internal.InternalEntityQueryEnumerator(entityQuery); + _currentEntityIndex = -1; _endEntityIndex = -1; + _typeHandle = typeHandle; _resolvedChunk = default; } - + public void Dispose() => _entityQueryEnumerator.Dispose(); + [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public bool MoveNext() { _currentEntityIndex++; + if (_currentEntityIndex >= _endEntityIndex) { if (_entityQueryEnumerator.MoveNextEntityRange(out bool movedToNewChunk, out global::Unity.Entities.ArchetypeChunk chunk, out int entityStartIndex, out int entityEndIndex)) { if (movedToNewChunk) + { _resolvedChunk = _typeHandle.Resolve(chunk); + } + _currentEntityIndex = entityStartIndex; _endEntityIndex = entityEndIndex; return true; } - return false; } - return true; } - + public Unity.Entities.QueryEnumerableWithEntity> Current => _resolvedChunk.Get(_currentEntityIndex); + public Enumerator GetEnumerator() => this; public void Reset() => throw new global::System.NotImplementedException(); object global::System.Collections.IEnumerator.Current => throw new global::System.NotImplementedException(); } - public static void CompleteDependencyBeforeRW(ref SystemState state) { state.EntityManager.CompleteDependencyBeforeRO(); } } - + TypeHandle __TypeHandle; global::Unity.Entities.EntityQuery __query_1641826537_0; struct TypeHandle { public IFE_1641826537_0.TypeHandle __IFE_1641826537_0_RW_TypeHandle; - [global::Unity.Collections.ReadOnly] - public Unity.Entities.EntityStorageInfoLookup __EntityStorageInfoLookup; + [global::Unity.Collections.ReadOnly] public Unity.Entities.EntityStorageInfoLookup __EntityStorageInfoLookup; [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public void __AssignHandles(ref global::Unity.Entities.SystemState state) { __IFE_1641826537_0_RW_TypeHandle = new IFE_1641826537_0.TypeHandle(ref state, isReadOnly: false); __EntityStorageInfoLookup = state.GetEntityStorageInfoLookup(); } + } - [global::System.Runtime.CompilerServices.MethodImpl(global::System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] void __AssignQueries(ref global::Unity.Entities.SystemState state) { - __query_1641826537_0 = state.GetEntityQuery(new global::Unity.Entities.EntityQueryDesc{All = new global::Unity.Entities.ComponentType[]{global::Unity.Entities.ComponentType.ReadOnly()}, Any = new global::Unity.Entities.ComponentType[]{}, None = new global::Unity.Entities.ComponentType[]{}, Disabled = new global::Unity.Entities.ComponentType[]{}, Absent = new global::Unity.Entities.ComponentType[]{}, Options = global::Unity.Entities.EntityQueryOptions.Default}); + var entityQueryBuilder = new global::Unity.Entities.EntityQueryBuilder(global::Unity.Collections.Allocator.Temp); + __query_1641826537_0 = + entityQueryBuilder + .WithAll() + .Build(ref state); + entityQueryBuilder.Reset(); + entityQueryBuilder.Dispose(); } - + public void OnCreateForCompiler(ref SystemState state) { __AssignQueries(ref state); __TypeHandle.__AssignHandles(ref state); } -} \ No newline at end of file +} diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.CodeFixes.dll b/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.CodeFixes.dll index 32b2db6..809bfce 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.CodeFixes.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.CodeFixes.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.CodeFixes.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.CodeFixes.pdb index 6aee79c..9edbb8f 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.CodeFixes.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.CodeFixes.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.dll b/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.dll index 4d465ff..acb253b 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.dll.meta b/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.dll.meta index 6939cdd..0d7c10c 100644 --- a/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.dll.meta +++ b/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.dll.meta @@ -69,4 +69,4 @@ PluginImporter: CPU: AnyCPU userData: assetBundleName: - assetBundleVariant: \ No newline at end of file + assetBundleVariant: diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.pdb index 2f9da40..3de068f 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.Analyzer.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.AspectGenerator.dll b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.AspectGenerator.dll index 407309b..1668588 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.AspectGenerator.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.AspectGenerator.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.AspectGenerator.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.AspectGenerator.pdb index 404dfdf..5369236 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.AspectGenerator.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.AspectGenerator.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.dll b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.dll index b2acd85..7b02376 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.dll.meta b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.dll.meta index 8b69201..e3cfd49 100644 --- a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.dll.meta +++ b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.dll.meta @@ -69,4 +69,4 @@ PluginImporter: CPU: AnyCPU userData: assetBundleName: - assetBundleVariant: \ No newline at end of file + assetBundleVariant: diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.pdb index 87a11de..c95e382 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.Common.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.JobEntityGenerator.dll b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.JobEntityGenerator.dll index d75a628..fe66d3f 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.JobEntityGenerator.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.JobEntityGenerator.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.JobEntityGenerator.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.JobEntityGenerator.pdb index f347f9b..e1fe4bf 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.JobEntityGenerator.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.JobEntityGenerator.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.Common.dll b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.Common.dll index 8719fbb..e0569b0 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.Common.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.Common.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.Common.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.Common.pdb index 276e452..268d807 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.Common.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.Common.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.dll b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.dll index 5b642b1..c64c553 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.pdb index 4e1f9ce..767ea55 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.EntityQueryBulkOperations.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.dll b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.dll index 12aff65..ff3caef 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.pdb index 4e00214..171c179 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.LambdaJobs.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.dll b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.dll index 3faa1b2..a3d76fb 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.pdb index 9f142e9..4a18e4a 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.Query.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.dll b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.dll index 2c6d84a..efc6dd6 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.pdb index ddf88b1..800f987 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.QueryBuilder.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.dll b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.dll index 9d9340a..89d472c 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.pdb index 308d9c6..06d968a 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.SystemAPI.pdb differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.dll b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.dll index 997267c..a84c6b4 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.dll and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.dll differ diff --git a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.pdb b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.pdb index b2158fa..83e45e6 100644 Binary files a/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.pdb and b/Unity.Entities/SourceGenerators/Unity.Entities.SourceGen.SystemGenerator.pdb differ diff --git a/Unity.Entities/Stubs/Unity.Assertions/Assert.cs b/Unity.Entities/Stubs/Unity.Assertions/Assert.cs index c8792c8..b6517f5 100644 --- a/Unity.Entities/Stubs/Unity.Assertions/Assert.cs +++ b/Unity.Entities/Stubs/Unity.Assertions/Assert.cs @@ -75,11 +75,7 @@ public static void IsNull(T value) where T : class { if(value != null) { -#if UNITY_DOTSRUNTIME - IsTrue(ReferenceEquals(value, null)); -#else UnityEngine.Assertions.Assert.IsNull(value); -#endif } } @@ -94,11 +90,7 @@ public static void IsNull(T value, string message) where T : class { if (value != null) { -#if UNITY_DOTSRUNTIME - IsTrue(ReferenceEquals(value, null), message); -#else UnityEngine.Assertions.Assert.IsNull(value, message); -#endif } } @@ -112,11 +104,7 @@ public static void IsNotNull(T value) where T : class { if (value == null) { -#if UNITY_DOTSRUNTIME - IsFalse(ReferenceEquals(value, null)); -#else UnityEngine.Assertions.Assert.IsNotNull(value); -#endif } } @@ -131,11 +119,7 @@ public static void IsNotNull(T value, string message) where T : class { if (value == null) { -#if UNITY_DOTSRUNTIME - IsFalse(ReferenceEquals(value, null), message); -#else UnityEngine.Assertions.Assert.IsNotNull(value, message); -#endif } } @@ -200,11 +184,7 @@ public static void AreEqual(T expected, T actual, string message) where T: IE { if (!expected.Equals(actual)) { -#if UNITY_DOTSRUNTIME - UnityEngine.Assertions.Assert.AreEqual(expected, actual); -#else UnityEngine.Assertions.Assert.AreEqual(expected, actual, message); -#endif } } @@ -233,11 +213,7 @@ public static void AreNotEqual(T expected, T actual, string message) where T: { if (expected.Equals(actual)) { -#if UNITY_DOTSRUNTIME - UnityEngine.Assertions.Assert.AreNotEqual(expected, actual); -#else UnityEngine.Assertions.Assert.AreNotEqual(expected, actual, message); -#endif } } diff --git a/Unity.Entities/Stubs/Unity/Debug.cs b/Unity.Entities/Stubs/Unity/Debug.cs index 0bad045..2d4ecde 100644 --- a/Unity.Entities/Stubs/Unity/Debug.cs +++ b/Unity.Entities/Stubs/Unity/Debug.cs @@ -1,7 +1,5 @@ using System; -#if !UNITY_DOTSRUNTIME using UnityObject = UnityEngine.Object; -#endif namespace Unity { @@ -18,7 +16,6 @@ public static void Log(object message) => public static void LogException(Exception exception) => UnityEngine.Debug.LogException(exception); - #if !UNITY_DOTSRUNTIME public static void LogError(object message, UnityObject context) => UnityEngine.Debug.LogError(message, context); public static void LogWarning(object message, UnityObject context) => @@ -27,6 +24,5 @@ public static void Log(object message, UnityObject context) => UnityEngine.Debug.Log(message, context); public static void LogException(Exception exception, UnityObject context) => UnityEngine.Debug.LogException(exception, context); - #endif } } diff --git a/Unity.Entities/SystemAPI.cs b/Unity.Entities/SystemAPI.cs index a9c3f42..d45e472 100644 --- a/Unity.Entities/SystemAPI.cs +++ b/Unity.Entities/SystemAPI.cs @@ -1,4 +1,4 @@ -using System; +using System; using Unity.Core; namespace Unity.Entities @@ -727,7 +727,6 @@ public static bool TryGetSingletonBuffer(out DynamicBuffer value, bool isR public static class ManagedAPI { #region Query -#if !UNITY_DOTSRUNTIME /// /// Can be used inside iteration to retrieve UnityEngine components like /// ScriptableObjects, MonoBehaviour and UnityEngine.Transform @@ -746,7 +745,6 @@ public struct UnityEngineComponent : IQueryTypeParameter where T : UnityEngin /// public UnityEngineComponent(T value) => Value = value; } -#endif #endregion #region ComponentData diff --git a/Unity.Entities/SystemAPIQueryBuilder.cs b/Unity.Entities/SystemAPIQueryBuilder.cs index fcda816..dfda182 100644 --- a/Unity.Entities/SystemAPIQueryBuilder.cs +++ b/Unity.Entities/SystemAPIQueryBuilder.cs @@ -196,7 +196,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithDisabledRW() => throw ThrowNotBuildException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present and enabled on matching entities. /// /// Mandatory component /// This query builder object, to allow chaining multiple method calls. @@ -204,7 +204,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithAll() => throw ThrowNotBuildException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present and enabled on matching entities. /// /// Mandatory component /// Mandatory component @@ -213,7 +213,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithAll() => throw ThrowNotBuildException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present and enabled on matching entities. /// /// Mandatory component /// Mandatory component @@ -223,7 +223,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithAll() => throw ThrowNotBuildException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present and enabled on matching entities. /// /// Mandatory component /// Mandatory component @@ -234,7 +234,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithAll() => throw ThrowNotBuildException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present and enabled on matching entities. /// /// Mandatory component /// Mandatory component @@ -246,7 +246,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithAll() => throw ThrowNotBuildException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present and enabled on matching entities. /// /// Mandatory component /// Mandatory component @@ -259,7 +259,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithAll() => throw ThrowNotBuildException(); /// - /// Specify all read-only component types that must be present. + /// Specify all read-only component types that must be present and enabled on matching entities. /// /// Mandatory component /// Mandatory component @@ -273,7 +273,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithAll() => throw ThrowNotBuildException(); /// - /// Specify all read-write component types that must be present. + /// Specify all read-write component types that must be present and enabled on matching entities. /// /// Mandatory component /// This query builder object, to allow chaining multiple method calls. @@ -281,7 +281,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithAllRW() => throw ThrowNotBuildException(); /// - /// Specify all read-write component types that must be present. + /// Specify all read-write component types that must be present and enabled on matching entities. /// /// Mandatory component /// Mandatory component @@ -290,7 +290,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithAllRW() => throw ThrowNotBuildException(); /// - /// Add a required [Chunk Component](xref:components-chunk) type to the query. + /// Add a required [Chunk Component](xref:components-chunk) type to the query, which must be enabled. /// /// /// Call this method on the query builder to find entities that have all the specified chunk components. Chunk @@ -305,7 +305,7 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithAllChunkComponent() => throw ThrowNotBuildException(); /// - /// Add a required [Chunk Component](xref:components-chunk) type to the query. + /// Add a required [Chunk Component](xref:components-chunk) type to the query, which must be enabled. /// /// /// Call this method on the query builder to find entities that have all the specified chunk components. Chunk @@ -541,6 +541,130 @@ public struct SystemAPIQueryBuilder public SystemAPIQueryBuilder WithNoneChunkComponent() => throw ThrowNotBuildException(); + /// + /// Specify all read-only component types that must be present, whether or not they are enabled on matching entities. + /// + /// Mandatory component + /// This query builder object, to allow chaining multiple method calls. + /// + public SystemAPIQueryBuilder WithPresent() => throw ThrowNotBuildException(); + + /// + /// Specify all read-only component types that must be present, whether or not they are enabled on matching entities. + /// + /// Mandatory component + /// Mandatory component + /// This query builder object, to allow chaining multiple method calls. + /// + public SystemAPIQueryBuilder WithPresent() => throw ThrowNotBuildException(); + + /// + /// Specify all read-only component types that must be present, whether or not they are enabled on matching entities. + /// + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// This query builder object, to allow chaining multiple method calls. + /// + public SystemAPIQueryBuilder WithPresent() => throw ThrowNotBuildException(); + + /// + /// Specify all read-only component types that must be present, whether or not they are enabled on matching entities. + /// + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// This query builder object, to allow chaining multiple method calls. + /// + public SystemAPIQueryBuilder WithPresent() => throw ThrowNotBuildException(); + + /// + /// Specify all read-only component types that must be present, whether or not they are enabled on matching entities. + /// + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// This query builder object, to allow chaining multiple method calls. + /// + public SystemAPIQueryBuilder WithPresent() => throw ThrowNotBuildException(); + + /// + /// Specify all read-only component types that must be present, whether or not they are enabled on matching entities. + /// + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// This query builder object, to allow chaining multiple method calls. + /// + public SystemAPIQueryBuilder WithPresent() => throw ThrowNotBuildException(); + + /// + /// Specify all read-only component types that must be present, whether or not they are enabled on matching entities. + /// + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// Mandatory component + /// This query builder object, to allow chaining multiple method calls. + /// + public SystemAPIQueryBuilder WithPresent() => throw ThrowNotBuildException(); + + /// + /// Specify all read-write component types that must be present, whether or not they are enabled on matching entities. + /// + /// Mandatory component + /// This query builder object, to allow chaining multiple method calls. + /// + public SystemAPIQueryBuilder WithPresentRW() => throw ThrowNotBuildException(); + + /// + /// Specify all read-write component types that must be present, whether or not they are enabled on matching entities. + /// + /// Mandatory component + /// Mandatory component + /// This query builder object, to allow chaining multiple method calls. + /// + public SystemAPIQueryBuilder WithPresentRW() => throw ThrowNotBuildException(); + + /// + /// Add a required [Chunk Component](xref:components-chunk) type to the query, whether or not it is enabled. + /// + /// + /// Call this method on the query builder to find entities that have all the specified chunk components. Chunk + /// components are a distinct component type, which are different from adding the same type as a standard + /// component. + /// + /// To add additional required Chunk Components, call this method multiple times. + /// + /// + /// Component type to use as a required, read-only Chunk Component + /// The builder object that invoked this method. + public SystemAPIQueryBuilder WithPresentChunkComponent() => throw ThrowNotBuildException(); + + /// + /// Add a required [Chunk Component](xref:components-chunk) type to the query, whether or not it is enabled. + /// + /// + /// Call this method on the query builder to find entities that have all the specified chunk components. Chunk + /// components are a distinct component type, which are different from adding the same type as a standard + /// component. + /// + /// To add additional required Chunk Components, call this method multiple times. + /// + /// + /// Component type to use as a required, read-write Chunk Component + /// The builder object that invoked this method. + public SystemAPIQueryBuilder WithPresentChunkComponentRW() => throw ThrowNotBuildException(); + /// /// Add component type requirement for a given aspect. /// diff --git a/Unity.Entities/SystemBaseRegistry.cs b/Unity.Entities/SystemBaseRegistry.cs index 63c4c68..8402227 100644 --- a/Unity.Entities/SystemBaseRegistry.cs +++ b/Unity.Entities/SystemBaseRegistry.cs @@ -119,9 +119,7 @@ class Managed { public static List s_StructTypes = null; public static List s_PendingRegistrations; - #if !UNITY_DOTSRUNTIME public static bool s_DisposeRegistered = false; - #endif } struct Dummy @@ -177,7 +175,6 @@ public static unsafe void AddUnmanagedSystemType(Type type, long typeHash, Forwa data.Construct(); // Arrange for domain unloads to wipe the pending registration list, which works around multiple domain reloads in sequence -#if !UNITY_DOTSRUNTIME if (!Managed.s_DisposeRegistered) { Managed.s_DisposeRegistered = true; @@ -191,7 +188,6 @@ public static unsafe void AddUnmanagedSystemType(Type type, long typeHash, Forwa Managed.s_PendingRegistrations = null; }; } -#endif } // The order/number here must match UnmanagedSystemFunctionType @@ -242,10 +238,7 @@ static void SelectBurstFn(out ulong result, out ulong defeatGc, ulong burstFn, F } else { -#if UNITY_DOTSRUNTIME_IL2CPP || ENABLE_IL2CPP - // Tiny IL2CPP does not handle reverse pinvoke wrapping for lambda functions - // and since try/catch isn't supported in Tiny IL2CPP either, there is no need - // to wrap the managedFn here. +#if ENABLE_IL2CPP defeatGc = (ulong)GCHandle.ToIntPtr(GCHandle.Alloc(managedFn)); result = (ulong)Marshal.GetFunctionPointerForDelegate(managedFn); #else diff --git a/Unity.Entities/SystemDependencySafetyUtility.cs b/Unity.Entities/SystemDependencySafetyUtility.cs index 29a6a53..b9f9f15 100644 --- a/Unity.Entities/SystemDependencySafetyUtility.cs +++ b/Unity.Entities/SystemDependencySafetyUtility.cs @@ -61,12 +61,7 @@ internal static bool FindSystemSchedulingErrors( JobHandle* handles = stackalloc JobHandle[kMaxHandles]; int* systemIds = stackalloc int[kMaxHandles]; -#if !UNITY_DOTSRUNTIME int mappingCount = Math.Min(JobsUtility.GetSystemIdMappings(handles, systemIds, kMaxHandles), kMaxHandles); -#else - // FIXME - int mappingCount = 0; -#endif // Filter out jobs created by current system. for (int i = 0; i < mappingCount; ++i) diff --git a/Unity.Entities/SystemState.cs b/Unity.Entities/SystemState.cs index 1469106..2440d30 100644 --- a/Unity.Entities/SystemState.cs +++ b/Unity.Entities/SystemState.cs @@ -73,8 +73,8 @@ private void CheckThis() #endregion #region Rarely accessed during System.OnUpdate depending on what they do (Cold) - internal SystemTypeIndex m_SystemTypeIndex; - + internal SystemTypeIndex m_SystemTypeIndex; + internal int m_SystemID; internal EntityManager m_EntityManager; @@ -503,13 +503,11 @@ internal void AfterUpdateRecordTiming() [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS")] internal static void InitSystemIdCell() { -#if !UNITY_DOTSRUNTIME if (s_SystemIdCellPtr.Data == IntPtr.Zero) { s_SystemIdCellPtr.Data = JobsUtility.GetSystemIdCellPtr(); SetCurrentSystemIdForJobDebugger(0); } -#endif } #endif @@ -517,14 +515,10 @@ internal static void InitSystemIdCell() [GenerateTestsForBurstCompatibility(RequiredUnityDefine = "ENABLE_UNITY_COLLECTIONS_CHECKS")] internal static int SetCurrentSystemIdForJobDebugger(int id) { -#if !UNITY_DOTSRUNTIME var ptr = *(int**)s_SystemIdCellPtr.UnsafeDataPointer; var old = *ptr; *ptr = id; return old; -#else - return 0; -#endif } #endif @@ -1239,6 +1233,11 @@ internal void RequireAnyForUpdate(EntityQuery* queries, int queryCount) var componentType = new ComponentType{ TypeIndex = archetypeQuery.Disabled[i], AccessModeType = (ComponentType.AccessMode)archetypeQuery.DisabledAccessMode[i] }; builder.WithDisabled(&componentType, 1); } + for (var i = 0; i < archetypeQuery.PresentCount; i++) + { + var componentType = new ComponentType{ TypeIndex = archetypeQuery.Present[i], AccessModeType = (ComponentType.AccessMode)archetypeQuery.PresentAccessMode[i] }; + builder.WithPresent(&componentType, 1); + } builder.WithOptions(archetypeQuery.Options); builder.FinalizeQueryInternal(); } diff --git a/Unity.Entities/Types/Archetype.cs b/Unity.Entities/Types/Archetype.cs index 081c48c..38d0fe5 100644 --- a/Unity.Entities/Types/Archetype.cs +++ b/Unity.Entities/Types/Archetype.cs @@ -26,7 +26,7 @@ internal enum ArchetypeFlags : ushort internal unsafe struct Archetype { public ArchetypeChunkData Chunks; - public UnsafePtrList ChunksWithEmptySlots; + public UnsafeList ChunksWithEmptySlots; public ChunkListMap FreeChunksBySharedComponents; public ComponentTypeInArchetype* Types; // Array with TypeCount elements @@ -134,9 +134,9 @@ public override string ToString() return info; } - public void AddToChunkList(Chunk *chunk, SharedComponentValues sharedComponentIndices, uint changeVersion, ref EntityComponentStore.ChunkListChanges changes) + public void AddToChunkList(ChunkIndex chunk, SharedComponentValues sharedComponentIndices, uint changeVersion, ref EntityComponentStore.ChunkListChanges changes) { - chunk->ListIndex = Chunks.Count; + chunk.ListIndex = Chunks.Count; if (Chunks.Count == Chunks.Capacity) { var newCapacity = (Chunks.Capacity == 0) ? 1 : (Chunks.Capacity * 2); @@ -162,11 +162,12 @@ public void AddToChunkList(Chunk *chunk, SharedComponentValues sharedComponentIn } } - public void RemoveFromChunkList(Chunk* chunk, ref EntityComponentStore.ChunkListChanges changes) + public void RemoveFromChunkList(ChunkIndex chunk, ref EntityComponentStore.ChunkListChanges changes) { - Chunks.RemoveAtSwapBack(chunk->ListIndex); - var chunkThatMoved = Chunks[chunk->ListIndex]; - chunkThatMoved->ListIndex = chunk->ListIndex; + var chunkListIndex = chunk.ListIndex; + Chunks.RemoveAtSwapBack(chunkListIndex); + var chunkThatMoved = Chunks[chunkListIndex]; + chunkThatMoved.ListIndex = chunkListIndex; fixed(Archetype* archetype = &this) { @@ -174,23 +175,23 @@ public void RemoveFromChunkList(Chunk* chunk, ref EntityComponentStore.ChunkList } } - void AddToChunkListWithEmptySlots(Chunk *chunk) + void AddToChunkListWithEmptySlots(ChunkIndex chunk) { - chunk->ListWithEmptySlotsIndex = ChunksWithEmptySlots.Length; + chunk.ListWithEmptySlotsIndex = ChunksWithEmptySlots.Length; ChunksWithEmptySlots.Add(chunk); } - void RemoveFromChunkListWithEmptySlots(Chunk *chunk) + void RemoveFromChunkListWithEmptySlots(ChunkIndex chunk) { - var index = chunk->ListWithEmptySlotsIndex; + var index = chunk.ListWithEmptySlotsIndex; Assert.IsTrue(index >= 0 && index < ChunksWithEmptySlots.Length); Assert.IsTrue(ChunksWithEmptySlots.Ptr[index] == chunk); ChunksWithEmptySlots.RemoveAtSwapBack(index); - if (chunk->ListWithEmptySlotsIndex < ChunksWithEmptySlots.Length) + if (index < ChunksWithEmptySlots.Length) { - var chunkThatMoved = ChunksWithEmptySlots.Ptr[chunk->ListWithEmptySlotsIndex]; - chunkThatMoved->ListWithEmptySlotsIndex = chunk->ListWithEmptySlotsIndex; + var chunkThatMoved = ChunksWithEmptySlots.Ptr[index]; + chunkThatMoved.ListWithEmptySlotsIndex = index; } } @@ -200,11 +201,11 @@ void RemoveFromChunkListWithEmptySlots(Chunk *chunk) /// - Does not check if chunk is locked. /// /// - internal void EmptySlotTrackingRemoveChunk(Chunk* chunk) + internal void EmptySlotTrackingRemoveChunk(ChunkIndex chunk) { fixed (Archetype* archetype = &this) { - Assert.AreEqual((ulong)archetype, (ulong)chunk->Archetype); + Assert.AreEqual((ulong)archetype, (ulong)EntityComponentStore->GetArchetype(chunk)); } if (NumSharedComponents == 0) RemoveFromChunkListWithEmptySlots(chunk); @@ -218,11 +219,11 @@ internal void EmptySlotTrackingRemoveChunk(Chunk* chunk) /// - Does not check if chunk is locked. /// /// - internal void EmptySlotTrackingAddChunk(Chunk* chunk) + internal void EmptySlotTrackingAddChunk(ChunkIndex chunk) { fixed (Archetype* archetype = &this) { - Assert.AreEqual((ulong)archetype, (ulong)chunk->Archetype); + Assert.AreEqual((ulong)archetype, (ulong)EntityComponentStore->GetArchetype(chunk)); } if (NumSharedComponents == 0) AddToChunkListWithEmptySlots(chunk); @@ -230,27 +231,20 @@ internal void EmptySlotTrackingAddChunk(Chunk* chunk) FreeChunksBySharedComponents.Add(chunk); } - internal Chunk* GetExistingChunkWithEmptySlots(SharedComponentValues sharedComponentValues) + internal ChunkIndex GetExistingChunkWithEmptySlots(SharedComponentValues sharedComponentValues) { if (NumSharedComponents == 0) { if (ChunksWithEmptySlots.Length != 0) { - var chunk = ChunksWithEmptySlots.Ptr[0]; - Assert.AreNotEqual(chunk->Count, chunk->Capacity); - return chunk; - } - } - else - { - var chunk = FreeChunksBySharedComponents.TryGet(sharedComponentValues, NumSharedComponents); - if (chunk != null) - { + var chunk = ChunksWithEmptySlots[0]; + Assert.AreNotEqual(chunk.Count, ChunkCapacity); return chunk; } } - return null; + // note: will be ChunkIndex.Null if none available. + return FreeChunksBySharedComponents.TryGet(sharedComponentValues, NumSharedComponents); } internal bool CompareMask(EntityQueryMask mask) diff --git a/Unity.Entities/Types/ArchetypeChunkData.cs b/Unity.Entities/Types/ArchetypeChunkData.cs index d4d9530..f3b7a6e 100644 --- a/Unity.Entities/Types/ArchetypeChunkData.cs +++ b/Unity.Entities/Types/ArchetypeChunkData.cs @@ -11,7 +11,7 @@ namespace Unity.Entities [DebuggerTypeProxy(typeof(ArchetypeChunkDataDebugView))] internal unsafe struct ArchetypeChunkData { - private Chunk** p; + void* m_Data; public int Capacity { get; private set; } // maximum number of chunks that can be tracked before Grow() must be called public int Count { get; private set; } // number of chunks currently tracked [0..Capacity] @@ -24,21 +24,23 @@ internal unsafe struct ArchetypeChunkData // type2: chunk0 chunk1 chunk2 ... // ... - ulong ChunkPtrSize => (ulong)(sizeof(Chunk*) * Capacity); + ulong ChunkIndicesSize => (ulong)(sizeof(ChunkIndex) * Capacity); ulong ChangeVersionSize => (ulong)(sizeof(uint) * ComponentCount * Capacity); ulong EntityCountSize => (ulong)(sizeof(int) * Capacity); ulong SharedComponentValuesSize => (ulong)(sizeof(int) * SharedComponentCount * Capacity); const ulong ComponentEnabledBitsSizePerComponentInChunk = (2 * sizeof(ulong)); // size of bits for ONE component in a chunk - ulong PaddingForEnabledBitAlignmentSize => 16 - ((ChunkPtrSize + ChangeVersionSize + EntityCountSize + SharedComponentValuesSize) % 16); // enabled bits must be 16-byte aligned + ulong PaddingForEnabledBitAlignmentSize => 16 - ((ChunkIndicesSize + ChangeVersionSize + EntityCountSize + SharedComponentValuesSize) % 16); // enabled bits must be 16-byte aligned public ulong ComponentEnabledBitsSizeTotalPerChunk => ComponentEnabledBitsSizePerComponentInChunk * (ulong)ComponentCount; // size of bits for ALL components in a chunk ulong ComponentEnabledBitsSize => ComponentEnabledBitsSizeTotalPerChunk * (ulong)Capacity; // size of bits for ALL components of ALL chunks public ulong ComponentEnabledBitsHierarchicalDataSizePerChunk => (ulong)(sizeof(int) * ComponentCount); // size of enabled bits hierarchical data for ONE chunk ulong ComponentEnabledBitsHierarchicalDataSize => (ComponentEnabledBitsHierarchicalDataSizePerChunk * (ulong) Capacity); // size of enabled bits hierarchical data for ALL chunks - ulong BufferSize => ChunkPtrSize + ChangeVersionSize + EntityCountSize + SharedComponentValuesSize + PaddingForEnabledBitAlignmentSize + ComponentEnabledBitsSize + ComponentEnabledBitsHierarchicalDataSize; + ulong BufferSize => ChunkIndicesSize + ChangeVersionSize + EntityCountSize + SharedComponentValuesSize + PaddingForEnabledBitAlignmentSize + ComponentEnabledBitsSize + ComponentEnabledBitsHierarchicalDataSize; + + ChunkIndex* ChunkIndices => (ChunkIndex*)m_Data; // ChangeVersions[ComponentCount * Capacity] // - Order version is ChangeVersion[0] which is ChangeVersion[Entity] - uint* ChangeVersions => (uint*)(((ulong)p) + ChunkPtrSize); + uint* ChangeVersions => (uint*)(((ulong)m_Data) + ChunkIndicesSize); // EntityCount[Capacity] int* EntityCount => (int*)(((ulong)ChangeVersions) + ChangeVersionSize); @@ -69,31 +71,31 @@ internal unsafe struct ArchetypeChunkData public ArchetypeChunkData(int componentCount, int sharedComponentCount) { - p = null; + m_Data = null; Capacity = 0; Count = 0; SharedComponentCount = sharedComponentCount; ComponentCount = componentCount; } - public Chunk* this[int index] => p[index]; + public ChunkIndex this[int index] => ChunkIndices[index]; public void Grow(int nextCapacity) { Assert.IsTrue(nextCapacity > Capacity); - ulong nextChunkPtrSize = (ulong)(sizeof(Chunk*) * nextCapacity); + ulong nextChunkIndicesSize = (ulong)(sizeof(ChunkIndex) * nextCapacity); ulong nextChangeVersionSize = (ulong)(sizeof(uint) * ComponentCount * nextCapacity); ulong nextEntityCountSize = (ulong)(sizeof(int) * nextCapacity); ulong nextSharedComponentValuesSize = (ulong)(sizeof(int) * SharedComponentCount * nextCapacity); - ulong paddingForEnabledBitAlignmentSize = 16 - ((nextChunkPtrSize + nextChangeVersionSize + nextEntityCountSize + nextSharedComponentValuesSize) % 16); // enabled bits must be 16-byte aligned + ulong paddingForEnabledBitAlignmentSize = 16 - ((nextChunkIndicesSize + nextChangeVersionSize + nextEntityCountSize + nextSharedComponentValuesSize) % 16); // enabled bits must be 16-byte aligned ulong nextComponentEnabledBitsSize = ComponentEnabledBitsSizeTotalPerChunk * (ulong)nextCapacity; ulong nextComponentEnabledBitsHierarchicalDataSize = ComponentEnabledBitsHierarchicalDataSizePerChunk * (ulong) nextCapacity; - ulong nextBufferSize = nextChunkPtrSize + nextChangeVersionSize + nextEntityCountSize + nextSharedComponentValuesSize + paddingForEnabledBitAlignmentSize + nextComponentEnabledBitsSize + nextComponentEnabledBitsHierarchicalDataSize; + ulong nextBufferSize = nextChunkIndicesSize + nextChangeVersionSize + nextEntityCountSize + nextSharedComponentValuesSize + paddingForEnabledBitAlignmentSize + nextComponentEnabledBitsSize + nextComponentEnabledBitsHierarchicalDataSize; ulong nextBufferPtr = (ulong)Memory.Unmanaged.Allocate((long)nextBufferSize, 16, Allocator.Persistent); - Chunk** nextChunkData = (Chunk**)nextBufferPtr; - nextBufferPtr += nextChunkPtrSize; + var nextChunkData = (void*)nextBufferPtr; + nextBufferPtr += nextChunkIndicesSize; uint* nextChangeVersions = (uint*)nextBufferPtr; nextBufferPtr += nextChangeVersionSize; int* nextEntityCount = (int*)nextBufferPtr; @@ -105,18 +107,18 @@ public void Grow(int nextCapacity) byte* nextComponentEnabledBitsValues = (byte*)nextBufferPtr; nextBufferPtr += nextComponentEnabledBitsSize; int* nextComponentEnabledBitsHierarchicalDataValues = (int*)nextBufferPtr; - nextBufferPtr += nextComponentEnabledBitsHierarchicalDataSize; + // nextBufferPtr += nextComponentEnabledBitsHierarchicalDataSize; int prevCount = Count; int prevCapacity = Capacity; - Chunk** prevChunkData = p; + var prevChunkData = m_Data; uint* prevChangeVersions = ChangeVersions; int* prevEntityCount = EntityCount; int* prevSharedComponentValues = SharedComponentValues; v128* prevComponentEnabledBitsValues = ComponentEnabledBits; int* prevComponentEnabledBitsHierarchicalDataValues = ComponentEnabledBitsHierarchicalData; - UnsafeUtility.MemCpy(nextChunkData, prevChunkData, (sizeof(Chunk*) * prevCount)); + UnsafeUtility.MemCpy(nextChunkData, prevChunkData, sizeof(ChunkIndex) * prevCount); for (int i = 0; i < ComponentCount; i++) UnsafeUtility.MemCpy(nextChangeVersions + (i * nextCapacity), prevChangeVersions + (i * prevCapacity), sizeof(uint) * Count); @@ -130,15 +132,15 @@ public void Grow(int nextCapacity) UnsafeUtility.MemCpy(nextComponentEnabledBitsHierarchicalDataValues, prevComponentEnabledBitsHierarchicalDataValues, (long)ComponentEnabledBitsHierarchicalDataSize); - Memory.Unmanaged.Free(p, Allocator.Persistent); + Memory.Unmanaged.Free(m_Data, Allocator.Persistent); - p = nextChunkData; + m_Data = nextChunkData; Capacity = nextCapacity; } public bool InsideAllocation(ulong addr) { - ulong startAddr = (ulong)p; + ulong startAddr = (ulong)m_Data; return (addr >= startAddr) && (addr <= (startAddr + BufferSize)); } @@ -316,11 +318,11 @@ public void SetChunkEntityCount(int chunkIndex, int count) EntityCount[chunkIndex] = count; } - public void Add(Chunk* chunk, SharedComponentValues sharedComponentIndices, uint changeVersion) + public void Add(ChunkIndex chunk, SharedComponentValues sharedComponentIndices, uint changeVersion) { var chunkIndex = Count++; - p[chunkIndex] = chunk; + ChunkIndices[chunkIndex] = chunk; for (int i = 0; i < SharedComponentCount; i++) SharedComponentValues[(i * Capacity) + chunkIndex] = sharedComponentIndices[i]; @@ -329,7 +331,7 @@ public void Add(Chunk* chunk, SharedComponentValues sharedComponentIndices, uint for (int i = 0; i < ComponentCount; i++) ChangeVersions[(i * Capacity) + chunkIndex] = changeVersion; - EntityCount[chunkIndex] = chunk->Count; + EntityCount[chunkIndex] = chunk.Count; } public void MoveChunks(in ArchetypeChunkData srcChunks) @@ -337,14 +339,14 @@ public void MoveChunks(in ArchetypeChunkData srcChunks) if (Capacity < Count + srcChunks.Count) Grow(Count + srcChunks.Count); - UnsafeUtility.MemCpy(p + Count, srcChunks.p, sizeof(Chunk*) * srcChunks.Count); + UnsafeUtility.MemCpy(ChunkIndices + Count, srcChunks.ChunkIndices, sizeof(ChunkIndex) * srcChunks.Count); Count += srcChunks.Count; } public void AddToCachedChunkList(ref UnsafeCachedChunkList chunkList, int matchingArchetypeIndex, int startIndex = 0) { - chunkList.Append(p + startIndex, Count - startIndex, matchingArchetypeIndex); + chunkList.Append(ChunkIndices + startIndex, Count - startIndex, matchingArchetypeIndex); } public void RemoveAtSwapBack(int chunkIndex) @@ -354,7 +356,7 @@ public void RemoveAtSwapBack(int chunkIndex) if (chunkIndex == Count) return; - p[chunkIndex] = p[Count]; + ChunkIndices[chunkIndex] = ChunkIndices[Count]; for (int i = 0; i < SharedComponentCount; i++) SharedComponentValues[(i * Capacity) + chunkIndex] = SharedComponentValues[(i * Capacity) + Count]; @@ -376,8 +378,8 @@ public void RemoveAtSwapBack(int chunkIndex) public void Dispose() { - Memory.Unmanaged.Free(p, Allocator.Persistent); - p = null; + Memory.Unmanaged.Free(m_Data, Allocator.Persistent); + m_Data = null; Capacity = 0; Count = 0; } diff --git a/Unity.Entities/Types/AspectType.cs b/Unity.Entities/Types/AspectType.cs index 4369137..4c4aa63 100644 --- a/Unity.Entities/Types/AspectType.cs +++ b/Unity.Entities/Types/AspectType.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using Unity.Collections; @@ -108,9 +107,7 @@ public override string ToString() if (TypeIndex == 0) return "None"; -#if !NET_DOTS return GetManagedType()?.Name ?? string.Empty; -#endif } /// @@ -143,4 +140,3 @@ public override int GetHashCode() } } } -#endif diff --git a/Unity.Entities/Types/AspectTypeInfo.cs b/Unity.Entities/Types/AspectTypeInfo.cs index 4937699..49f6535 100644 --- a/Unity.Entities/Types/AspectTypeInfo.cs +++ b/Unity.Entities/Types/AspectTypeInfo.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using Unity.Collections.LowLevel.Unsafe; @@ -23,4 +22,3 @@ public void Dispose() } } } -#endif diff --git a/Unity.Entities/Types/AspectTypeInfoManager.cs b/Unity.Entities/Types/AspectTypeInfoManager.cs index 3852b9f..fc3f33b 100644 --- a/Unity.Entities/Types/AspectTypeInfoManager.cs +++ b/Unity.Entities/Types/AspectTypeInfoManager.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Linq; using System.Reflection; @@ -44,6 +43,7 @@ static void InitializeAspectTypeInfo() var none = new UnsafeList(8, Allocator.Temp); var disabled = new UnsafeList(8, Allocator.Temp); var absent = new UnsafeList(8, Allocator.Temp); + var present = new UnsafeList(8, Allocator.Temp); addComponentRequirementsTo.Invoke(ref all); for (var j = 0; j != all.Length; ++j) @@ -54,6 +54,9 @@ static void InitializeAspectTypeInfo() all.Dispose(); any.Dispose(); none.Dispose(); + disabled.Dispose(); + absent.Dispose(); + present.Dispose(); s_AspectTypeInfo.Data.AspectTypes.AddNoResize(aspectType); } @@ -132,4 +135,3 @@ static bool ContainAnyExcludeComponents(NativeArray componentType } } } -#endif diff --git a/Unity.Entities/Types/BufferHeader.cs b/Unity.Entities/Types/BufferHeader.cs index 1de940a..40c7f32 100644 --- a/Unity.Entities/Types/BufferHeader.cs +++ b/Unity.Entities/Types/BufferHeader.cs @@ -122,20 +122,20 @@ public static void FreeBufferPtr(void* ptr) // After cloning two worlds have access to the same malloc'ed buffer pointer leading to double deallocate etc. // So after cloning, just allocate all malloc based buffers and copy the data. - public static void PatchAfterCloningChunk(Chunk* chunk) + public static void PatchAfterCloningChunk(Archetype* archetype, byte* chunkBuffer, int entityCount) { - for (int i = 0; i < chunk->Archetype->TypesCount; ++i) + for (int i = 0, archetypeTypesCount = archetype->TypesCount; i < archetypeTypesCount; ++i) { - var type = chunk->Archetype->Types[i]; + var type = archetype->Types[i]; if (!type.IsBuffer) continue; ref readonly var ti = ref TypeManager.GetTypeInfo(type.TypeIndex); - var sizeOf = chunk->Archetype->SizeOfs[i]; - var offset = chunk->Archetype->Offsets[i]; - for (var j = 0; j < chunk->Count; ++j) + var sizeOf = archetype->SizeOfs[i]; + var offset = archetype->Offsets[i]; + for (var j = 0; j < entityCount; ++j) { var offsetOfBuffer = offset + sizeOf * j; - var header = (BufferHeader*)(chunk->Buffer + offsetOfBuffer); + var header = (BufferHeader*)(chunkBuffer + offsetOfBuffer); if (header->Pointer != null) // hoo boy, it's a malloc { BufferHeader newHeader = *header; diff --git a/Unity.Entities/Types/BurstCompatibleTypes.cs b/Unity.Entities/Types/BurstCompatibleTypes.cs index 14ab304..f644783 100644 --- a/Unity.Entities/Types/BurstCompatibleTypes.cs +++ b/Unity.Entities/Types/BurstCompatibleTypes.cs @@ -37,6 +37,14 @@ public void AddComponentRequirementsTo(ref UnsafeList all) { } + public void CompleteDependencyBeforeRO(ref SystemState state) + { + } + + public void CompleteDependencyBeforeRW(ref SystemState state) + { + } + BurstCompatibleAspect IAspectCreate.CreateAspect(Entity entity, ref SystemState system) { throw new System.NotImplementedException(); @@ -47,5 +55,6 @@ BurstCompatibleAspect IAspectCreate.CreateAspect(Entity e [DisableAutoCreation] partial struct BurstCompatibleSystem : ISystem { - } + + } } diff --git a/Unity.Entities/Types/Chunk.cs b/Unity.Entities/Types/Chunk.cs index 26aabde..5d15245 100644 --- a/Unity.Entities/Types/Chunk.cs +++ b/Unity.Entities/Types/Chunk.cs @@ -1,8 +1,14 @@ +// #define ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + using System; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using Unity.Assertions; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; + +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS +using Unity.Profiling; +using UnityEngine.Assertions; +#endif + namespace Unity.Entities { [Flags] @@ -14,29 +20,266 @@ internal enum ChunkFlags TempAssertWillDestroyAllInLinkedEntityGroup = 1 << 2 } + struct ChunkIndex : IComparable + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + static ProfilerCounterOptions AccessCounterOptions => ProfilerCounterOptions.FlushOnEndOfFrame | ProfilerCounterOptions.ResetToZeroOnFlush; + static readonly ProfilerCounterValue k_AccessCounterSequenceNumber = new(ProfilerCategory.Scripts, "Chunk.SequenceNumber Access", + ProfilerMarkerDataUnit.Count, AccessCounterOptions); + static readonly ProfilerCounterValue k_AccessCounterListIndex = new(ProfilerCategory.Scripts, "Chunk.ListIndex Access", + ProfilerMarkerDataUnit.Count, AccessCounterOptions); + static readonly ProfilerCounterValue k_AccessCounterCount = new(ProfilerCategory.Scripts, "Chunk.Count Access", + ProfilerMarkerDataUnit.Count, AccessCounterOptions); + static readonly ProfilerCounterValue k_AccessCounterListWithEmptySlotsIndex = new(ProfilerCategory.Scripts, "Chunk.ListWithEmptySlotsIndex Access", + ProfilerMarkerDataUnit.Count, AccessCounterOptions); + static readonly ProfilerCounterValue k_AccessCounterMetaChunkEntity = new(ProfilerCategory.Scripts, "Chunk.MetaChunkEntity Access", + ProfilerMarkerDataUnit.Count, AccessCounterOptions); + static readonly ProfilerCounterValue k_AccessCounterBuffer = new(ProfilerCategory.Scripts, "Chunk.Buffer Access", + ProfilerMarkerDataUnit.Count, AccessCounterOptions); + static readonly ProfilerCounterValue k_AccessCounterFlags = new(ProfilerCategory.Scripts, "Chunk.Flags Access", + ProfilerMarkerDataUnit.Count, AccessCounterOptions); + + public static void ResetProfilerCounters() + { + Assert.AreEqual(AccessCounterOptions, ProfilerCounterOptions.None, "Toggle this value depending on if the counters are being used in a test or in the profiler"); + k_AccessCounterSequenceNumber.Value = 0; + k_AccessCounterListIndex.Value = 0; + k_AccessCounterCount.Value = 0; + k_AccessCounterListWithEmptySlotsIndex.Value = 0; + k_AccessCounterMetaChunkEntity.Value = 0; + k_AccessCounterBuffer.Value = 0; + k_AccessCounterFlags.Value = 0; + } + + public static void ReportProfilerCounters() + { + Debug.Log($"SequenceNumber {k_AccessCounterSequenceNumber.Value}"); + Debug.Log($"ListIndex {k_AccessCounterListIndex.Value}"); + Debug.Log($"Count {k_AccessCounterCount.Value}"); + Debug.Log($"ListWithEmptySlotsIndex {k_AccessCounterListWithEmptySlotsIndex.Value}"); + Debug.Log($"MetaChunkEntity {k_AccessCounterMetaChunkEntity.Value}"); + Debug.Log($"Buffer {k_AccessCounterBuffer.Value}"); + Debug.Log($"Flags {k_AccessCounterFlags.Value}"); + } +#endif + + int Value; + + public static ChunkIndex Null => new(); + + public ChunkIndex(int value) => Value = value; + public static implicit operator int(ChunkIndex index) => index.Value; + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + internal unsafe Chunk* GetPtr() => EntityComponentStore.s_chunkStore.Data.GetChunkPointer(Value); + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public unsafe bool MatchesFilter(MatchingArchetype* match, ref EntityQueryFilter filter) + => match->ChunkMatchesFilter(ListIndex, ref filter); + + public static bool operator ==(ChunkIndex a, ChunkIndex b) => a.Value == b.Value; + public static bool operator !=(ChunkIndex a, ChunkIndex b) => !(a == b); + + public static bool operator <(ChunkIndex a, ChunkIndex b) => a.Value < b.Value; + public static bool operator >(ChunkIndex a, ChunkIndex b) => a.Value > b.Value; + + public bool Equals(ChunkIndex other) => Value == other.Value; + public override bool Equals(object obj) => obj is ChunkIndex other && Equals(other); + public override int GetHashCode() => Value; + + internal ulong SequenceNumber + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterSequenceNumber.Value += 1; +#endif + unsafe + { + return GetPtr()->SequenceNumber; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterSequenceNumber.Value += 1; +#endif + unsafe + { + GetPtr()->SequenceNumber = value; + } + } + } + + internal int ListIndex + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterListIndex.Value += 1; +#endif + unsafe + { + return EntityComponentStore.PerChunkArray.ChunkData[Value].ListIndex; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterListIndex.Value += 1; +#endif + unsafe + { + EntityComponentStore.PerChunkArray.ChunkData[Value].ListIndex = value; + } + } + } + + internal int Count + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterCount.Value += 1; +#endif + unsafe + { + return EntityComponentStore.PerChunkArray.ChunkData[Value].EntityCount; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterCount.Value += 1; +#endif + unsafe + { + EntityComponentStore.PerChunkArray.ChunkData[Value].EntityCount = value; + } + } + } + + internal int ListWithEmptySlotsIndex + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterListWithEmptySlotsIndex.Value += 1; +#endif + unsafe + { + return GetPtr()->ListWithEmptySlotsIndex; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterListWithEmptySlotsIndex.Value += 1; +#endif + unsafe + { + GetPtr()->ListWithEmptySlotsIndex = value; + } + } + } + + internal Entity MetaChunkEntity + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterMetaChunkEntity.Value += 1; +#endif + unsafe + { + return GetPtr()->metaChunkEntity; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterMetaChunkEntity.Value += 1; +#endif + unsafe + { + GetPtr()->metaChunkEntity = value; + } + } + } + + internal unsafe byte* Buffer + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterBuffer.Value += 1; +#endif + return GetPtr()->Buffer; + } + } + + internal uint Flags + { + [MethodImpl(MethodImplOptions.AggressiveInlining)] + get + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterFlags.Value += 1; +#endif + unsafe + { + return GetPtr()->Flags; + } + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + set + { +#if ENABLE_UNITY_CHUNK_METADATA_ACCESSOR_COUNTERS + k_AccessCounterFlags.Value += 1; +#endif + unsafe + { + GetPtr()->Flags = value; + } + } + } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public int CompareTo(ChunkIndex other) + { + return Value.CompareTo(other.Value); + } + } + [StructLayout(LayoutKind.Explicit)] internal unsafe struct Chunk { // Chunk header START + + // The following field is only used during serialization and won't contain valid data at runtime. + // This is part of a larger refactor, and this field will eventually be removed from this struct. [FieldOffset(0)] - public Archetype* Archetype; - // 4-byte padding on 32-bit architectures here + public int ArchetypeIndexForSerialization; + // 4-byte padding to keep the file format compatible until further changes to the header. [FieldOffset(8)] public Entity metaChunkEntity; - // This is meant as read-only. - // EntityComponentStore.SetChunkCount should be used to change the count. + // The following field is only used during serialization and won't contain valid data at runtime. + // This is part of a larger refactor, and this field will eventually be removed from this struct. [FieldOffset(16)] - public int Count; - [FieldOffset(20)] - public int Capacity; - - /// - /// The index of this Chunk within its ArchetypeChunkData's chunk list - /// - [FieldOffset(24)] - public int ListIndex; + public int CountForSerialization; [FieldOffset(28)] public int ListWithEmptySlotsIndex; @@ -45,10 +288,6 @@ internal unsafe struct Chunk [FieldOffset(32)] public uint Flags; - // Index of chunk in global ChunkStore - [FieldOffset(36)] - public int ChunkstoreIndex; - // SequenceNumber is a unique number for each chunk, across all worlds. (Chunk* is not guranteed unique, in particular because chunk allocations are pooled) [FieldOffset(kSerializedHeaderSize)] public ulong SequenceNumber; @@ -57,7 +296,6 @@ internal unsafe struct Chunk // It is cleared on write to disk, it is a global in memory sequence ID used for comparing chunks. public const int kSerializedHeaderSize = 40; - // Chunk header END // Component data buffer @@ -71,57 +309,6 @@ internal unsafe struct Chunk public const int kBufferSize = kChunkSize - kBufferOffset; public const int kMaximumEntitiesPerChunk = kBufferSize / 8; - public int UnusedCount => Capacity - Count; - - public uint GetChangeVersion(int indexInArchetype) - { - return Archetype->Chunks.GetChangeVersion(indexInArchetype, ListIndex); - } - - public uint GetOrderVersion() - { - return Archetype->Chunks.GetOrderVersion(ListIndex); - } - - public void SetChangeVersion(int indexInArchetype, uint version) - { - Archetype->Chunks.SetChangeVersion(indexInArchetype, ListIndex, version); - } - - public void SetAllChangeVersions(uint version) - { - Archetype->Chunks.SetAllChangeVersion(ListIndex, version); - } - - public void SetOrderVersion(uint version) - { - Archetype->Chunks.SetOrderVersion(ListIndex, version); - } - - public int GetSharedComponentValue(int sharedComponentIndexInArchetype) - { - return Archetype->Chunks.GetSharedComponentValue(sharedComponentIndexInArchetype, ListIndex); - } - - public SharedComponentValues SharedComponentValues => Archetype->Chunks.GetSharedComponentValues(ListIndex); - - public static int GetChunkBufferSize() - { - // The amount of available space in a chunk is the max chunk size, kChunkSize, - // minus the size of the Chunk's metadata stored in the fields preceding Chunk.Buffer - return kChunkSize - kBufferOffset; - } - - public bool MatchesFilter(MatchingArchetype* match, ref EntityQueryFilter filter) - { - return match->ChunkMatchesFilter(ListIndex, ref filter); - } - - public int GetSharedComponentIndex(MatchingArchetype* match, int indexInEntityQuery) - { - var sharedComponentIndexInArchetype = match->IndexInArchetype[indexInEntityQuery]; - var sharedComponentIndexOffset = sharedComponentIndexInArchetype - match->Archetype->FirstSharedComponent; - return GetSharedComponentValue(sharedComponentIndexOffset); - } + public const int kChunkBufferSize = kChunkSize - kBufferOffset; } } diff --git a/Unity.Entities/Types/EntityArchetype.cs b/Unity.Entities/Types/EntityArchetype.cs index 7bc1a92..cc61d54 100644 --- a/Unity.Entities/Types/EntityArchetype.cs +++ b/Unity.Entities/Types/EntityArchetype.cs @@ -103,11 +103,7 @@ public override int GetHashCode() /// A string representation of this archetype. public override string ToString() { -#if !NET_DOTS return EntityManager.EntityManagerDebug.GetArchetypeDebugString(Archetype); -#else - return String.Empty; -#endif } /// diff --git a/Unity.Entities/Types/EntityDebugProxy.cs b/Unity.Entities/Types/EntityDebugProxy.cs index a810e51..d17218f 100644 --- a/Unity.Entities/Types/EntityDebugProxy.cs +++ b/Unity.Entities/Types/EntityDebugProxy.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Diagnostics; using Unity.Collections; using Unity.Jobs.LowLevel.Unsafe; @@ -11,7 +11,6 @@ namespace Unity.Entities /// class EntityDebugProxy { -#if !NET_DOTS [DebuggerBrowsable(DebuggerBrowsableState.Never)] Entity _Entity; [DebuggerBrowsable(DebuggerBrowsableState.Never)] @@ -68,10 +67,8 @@ public Entity_[] Worlds return proxy.ToArray(); } } -#endif } -#if !NET_DOTS /// /// Entity debug proxy when the world is explicitly known /// @@ -133,5 +130,4 @@ public override string ToString() public object[] Components => _Access.GetComponents(_Entity); public World World => _Access.GetWorld(); } -#endif } diff --git a/Unity.Entities/Types/FastEquality.cs b/Unity.Entities/Types/FastEquality.cs index b013e4c..23725f4 100644 --- a/Unity.Entities/Types/FastEquality.cs +++ b/Unity.Entities/Types/FastEquality.cs @@ -108,9 +108,6 @@ static internal void Shutdown() s_ComponentDelegates.Clear(); } - // While UNITY_DOTSRUNTIME not using Tiny BCL can compile most of this code, UnsafeUtility doesn't currently provide a - // FieldOffset method so we disable for UNITY_DOTSRUNTIME rather than NET_DOTS -#if !UNITY_DOTSRUNTIME static readonly ProfilerMarker ManagedEqualsMarker = new ProfilerMarker("FastEquality.ManagedEquals with IPropertyVisitor fallback (Missing IEquatable interface)"); internal static TypeInfo CreateTypeInfo(Dictionary> cache = null) where T : struct @@ -195,7 +192,7 @@ private unsafe static TypeInfo CreateManagedTypeInfo(Type t) ushort equalsDelegateIndex = TypeInfo.Null.EqualsDelegateIndex; ushort getHashCodeDelegateIndex = TypeInfo.Null.GetHashCodeDelegateIndex; - if (t.IsClass) + if (!UnsafeUtility.IsUnmanaged(t)) { if (typeof(IEquatable<>).MakeGenericType(t).IsAssignableFrom(t)) { @@ -312,10 +309,8 @@ private static List FindFields(Type type, Dictionary /// Returns the hash code for a managed component. Internally, this is exclusively used for managed shared @@ -349,9 +344,7 @@ public static unsafe int ManagedGetHashCode(object lhs, TypeInfo typeInfo) return hash; } -#endif -#if !UNITY_DOTSRUNTIME [BurstDiscard] static unsafe void GetHashCodeUsingDelegate(void* dataPtr, TypeInfo typeInfo, ref bool didWork, ref int hash) { @@ -362,18 +355,15 @@ static unsafe void GetHashCodeUsingDelegate(void* dataPtr, TypeInfo typeInfo, re didWork = true; } } -#endif [GenerateTestsForBurstCompatibility] internal static unsafe int GetHashCode(void* dataPtr, in TypeInfo typeInfo) { -#if !UNITY_DOTSRUNTIME int hash = 0; bool didWork = false; GetHashCodeUsingDelegate(dataPtr, typeInfo, ref didWork, ref hash); if(didWork) return hash; -#endif return GetHashCodeBlittable(dataPtr, in typeInfo); } @@ -406,7 +396,6 @@ internal static unsafe int Hash32(byte* ptr, int length, int seed = 0) return hash; } -#if !UNITY_DOTSRUNTIME /// /// Compares two managed component types. /// @@ -427,9 +416,7 @@ public static unsafe bool ManagedEquals(object lhs, object rhs, TypeInfo typeInf return new ManagedObjectEqual().CompareEqual(lhs, rhs); } } -#endif -#if !UNITY_DOTSRUNTIME [BurstDiscard] static unsafe void EqualsUsingDelegate(void* lhsPtr, void* rhsPtr, TypeInfo typeInfo, ref bool didWork, ref int result) { @@ -441,7 +428,6 @@ static unsafe void EqualsUsingDelegate(void* lhsPtr, void* rhsPtr, TypeInfo type didWork = true; } } -#endif /// /// Compares two component types. @@ -453,13 +439,11 @@ static unsafe void EqualsUsingDelegate(void* lhsPtr, void* rhsPtr, TypeInfo type [GenerateTestsForBurstCompatibility] public static unsafe bool Equals(void* lhsPtr, void* rhsPtr, in TypeInfo typeInfo) { -#if !UNITY_DOTSRUNTIME int result = 0; bool didWork = false; EqualsUsingDelegate(lhsPtr, rhsPtr, typeInfo, ref didWork, ref result); if (didWork) return result == 0; -#endif return EqualsBlittable(lhsPtr, rhsPtr, in typeInfo); } @@ -481,7 +465,6 @@ private static unsafe bool EqualsBlittable(void* lhsPtr, void* rhsPtr, in TypeIn } -#if !UNITY_DOTSRUNTIME private static bool TypeUsesDelegates(Type t) { #if !UNITY_DISABLE_MANAGED_COMPONENTS @@ -524,6 +507,5 @@ public static void AddExtraAOTTypes(Type type, HashSet output) output.Add(typeof(GetHashCodeImpl<>).MakeGenericType(type).ToString()); } } -#endif } } diff --git a/Unity.Entities/Types/TypeHash.cs b/Unity.Entities/Types/TypeHash.cs index 62ab76f..5c71184 100644 --- a/Unity.Entities/Types/TypeHash.cs +++ b/Unity.Entities/Types/TypeHash.cs @@ -1,11 +1,9 @@ -#if !NET_DOTS using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Runtime.InteropServices; using UnityEngine.Assertions; -#endif using Unity.Collections; namespace Unity.Entities @@ -119,21 +117,21 @@ public static ulong CombineFNV1A64(ulong hash, ulong value) return hash; } -#if !NET_DOTS + // Todo: Remove this. DOTS Runtime currently doesn't conform to these system types so don't inspect their fields private static readonly Type[] WorkaroundTypes = new Type[] { typeof(System.Guid) }; private static ulong HashType(Type type, Dictionary cache) { var hash = HashTypeName(type); -#if !UNITY_DOTSRUNTIME + // UnityEngine objects have their own serialization mechanism so exclude hashing the type's // internals and just hash its name+assemblyname (not fully qualified) if (TypeManager.UnityEngineObjectType?.IsAssignableFrom(type) == true) { return CombineFNV1A64(hash, FNV1A64(type.Assembly.GetName().Name)); } -#endif + if (type.IsGenericParameter || type.IsArray || type.IsPointer || type.IsPrimitive || type.IsEnum || WorkaroundTypes.Contains(type)) return hash; @@ -286,6 +284,5 @@ public static ulong CalculateMemoryOrdering(Type type, out bool hasCustomMemoryO return CalculateStableTypeHash(type, customAttributes, hashCache); } -#endif } } diff --git a/Unity.Entities/Types/TypeManager.cs b/Unity.Entities/Types/TypeManager.cs index 6df9fbc..4d9284c 100644 --- a/Unity.Entities/Types/TypeManager.cs +++ b/Unity.Entities/Types/TypeManager.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; -#if !NET_DOTS using System.Linq; -#endif using Unity.Burst; using System.Reflection; using System.Runtime.CompilerServices; @@ -1394,10 +1392,19 @@ public static void Initialize() if (s_Initialized) return; +#if UNITY_EDITOR + // Synchronous Burst compilation takes forever, so in the editor don't waste time for + // users who are trying to iterate and turn the option off while we generate type info + bool originalBurstCompileSyncValue = BurstCompiler.Options.EnableBurstCompileSynchronously; + BurstCompiler.Options.EnableBurstCompileSynchronously = false; + + var stopWatch = new Stopwatch(); + stopWatch.Start(); +#endif + s_Initialized = true; try { - #if UNITY_EDITOR if (!UnityEditorInternal.InternalEditorUtility.CurrentThreadIsMainThread()) throw new InvalidOperationException("Must be called from the main thread"); @@ -1443,7 +1450,6 @@ public static void Initialize() FastEquality.Initialize(); InitializeSystemsState(); - InitializeFieldInfoState(); // There are some types that must be registered first such as a null component and Entity RegisterSpecialComponents(); @@ -1473,6 +1479,16 @@ public static void Initialize() Shutdown(); throw; } +#if UNITY_EDITOR + finally + { + // Save the time since profiler might not catch the first frame. + stopWatch.Stop(); + Console.WriteLine($"TypeManager.Initialize took: {stopWatch.ElapsedMilliseconds}ms"); + + BurstCompiler.Options.EnableBurstCompileSynchronously = originalBurstCompileSyncValue; + } +#endif } static void InitializeSharedStatics() @@ -1501,7 +1517,7 @@ static void ShutdownSharedStatics() { SharedTypeInfos.Ref.Data = default; #if UNITY_DOTSRUNTIME - SharedDescendantIndices.Ref.Data = default; + SharedDescendantIndices.Ref.Data = default; #endif SharedDescendantCounts.Ref.Data = default; SharedEntityOffsetInfos.Ref.Data = default; @@ -1584,7 +1600,6 @@ public static void Shutdown() s_Types.Clear(); ShutdownSystemsState(); - ShutdownFieldInfoState(); #if !UNITY_DOTSRUNTIME s_FailedTypeBuildException = null; @@ -1737,6 +1752,14 @@ public static TypeIndex GetTypeIndex(Type type) return index; } +#if !UNITY_DOTSRUNTIME + [BurstDiscard] + internal static void ManagedEquals(ref T left, ref T right, TypeIndex typeIndex, out int result) where T : struct + { + result = FastEquality.ManagedEquals(left, right, GetFastEqualityTypeInfoPointer()[typeIndex.Index]) ? 1 : 0; + } +#endif + /// /// Compares two component instances to one another /// @@ -1752,7 +1775,16 @@ public static bool Equals(ref T left, ref T right) where T : struct { #if !UNITY_DOTSRUNTIME if (typeIndex.IsManagedType) - return FastEquality.Equals(UnsafeUtility.AddressOf(ref left), UnsafeUtility.AddressOf(ref right), in GetFastEqualityTypeInfoPointer()[typeIndex.Index]); + { + int result = -1; + ManagedEquals(ref left, ref right, typeIndex, out result); + + // We shouldn't be able to hit this codepath since Burst would have complained by now by the managed T use in the method + if(Burst.CompilerServices.Hint.Unlikely(result < 0)) + throw new ArgumentException($"Attempted to compare managed component type '{typeof(T)}' from Burst compiled method"); + + return result == 1; + } else #endif { @@ -1907,24 +1939,42 @@ public static bool Equals(object left, object right, TypeIndex typeIndex) return left == right; } - if (IsManagedComponent(typeIndex)) + if (IsManagedType(typeIndex)) { var typeInfo = GetFastEqualityTypeInfoPointer()[typeIndex.Index]; return FastEquality.ManagedEquals(left, right, typeInfo); } else { - var leftptr = (byte*)UnsafeUtility.PinGCObjectAndGetAddress(left, out var lhandle) + ObjectOffset; - var rightptr = (byte*)UnsafeUtility.PinGCObjectAndGetAddress(right, out var rhandle) + ObjectOffset; + // IL2CPP has a bug where we can fail to detect managed types correctly which will + // cause the code #else codepath to throw if it contains managed references, so we provide + // an alternative path until that is fixed (UUM-43422) + #if ENABLE_IL2CPP + var leftptr = (byte*)UnsafeUtility.PinGCObjectAndGetAddress(left, out var lhandle) + ObjectOffset; + var rightptr = (byte*)UnsafeUtility.PinGCObjectAndGetAddress(right, out var rhandle) + ObjectOffset; - var result = Equals(leftptr, rightptr, typeIndex); + var result = Equals(leftptr, rightptr, typeIndex); - UnsafeUtility.ReleaseGCObject(lhandle); - UnsafeUtility.ReleaseGCObject(rhandle); - return result; + UnsafeUtility.ReleaseGCObject(lhandle); + UnsafeUtility.ReleaseGCObject(rhandle); + + return result; + #else + var leftHandle = GCHandle.Alloc(left, GCHandleType.Pinned); + var rightHandle = GCHandle.Alloc(right, GCHandleType.Pinned); + var leftptr = (byte*)leftHandle.AddrOfPinnedObject(); + var rightptr = (byte*)rightHandle.AddrOfPinnedObject(); + + var result = Equals(leftptr, rightptr, typeIndex); + + leftHandle.Free(); + rightHandle.Free(); + + return result; + #endif } #else - return GetBoxedEquals(left, right, typeIndex.Index); + return GetBoxedEquals(left, right, typeIndex.Index); #endif } @@ -1938,17 +1988,40 @@ public static bool Equals(object left, object right, TypeIndex typeIndex) public static bool Equals(object left, void* right, TypeIndex typeIndex) { #if !UNITY_DOTSRUNTIME + + // IL2CPP has a bug where we can fail to detect managed types correctly which will + // cause the code #else codepath to throw if it contains managed references, so we provide + // an alternative path until that is fixed (UUM-43422) + #if ENABLE_IL2CPP var leftptr = (byte*)UnsafeUtility.PinGCObjectAndGetAddress(left, out var lhandle) + ObjectOffset; var result = Equals(leftptr, right, typeIndex); UnsafeUtility.ReleaseGCObject(lhandle); return result; + #else + var leftHandle = GCHandle.Alloc(left, GCHandleType.Pinned); + var leftptr = (byte*)leftHandle.AddrOfPinnedObject(); + + var result = Equals(leftptr, right, typeIndex); + + leftHandle.Free(); + return result; + #endif #else return GetBoxedEquals(left, right, typeIndex.Index); #endif } +#if !UNITY_DOTSRUNTIME + [BurstDiscard] + internal static void ManagedGetHashCode(ref T val, TypeIndex typeIndex, out int hash, out int did_work) where T : struct + { + did_work = 1; + hash = FastEquality.ManagedGetHashCode(val, GetFastEqualityTypeInfoPointer()[typeIndex.Index]); + } +#endif + /// /// Generates a hash for a given component instance /// @@ -1959,6 +2032,20 @@ public static bool Equals(object left, void* right, TypeIndex typeIndex) public static int GetHashCode(ref T val) where T : struct { var typeIndex = GetTypeIndex(); + +#if !UNITY_DOTSRUNTIME + if (typeIndex.IsManagedType) + { + ManagedGetHashCode(ref val, typeIndex, out int hash, out int did_work); + + // We shouldn't be able to hit this codepath since Burst would have complained by now by the managed T use in the method + if (Burst.CompilerServices.Hint.Unlikely(did_work == 0)) + throw new ArgumentException($"Attempted to GetHashCode using managed component type '{typeof(T)}' from Burst compiled method."); + + return hash; + } +#endif + return GetHashCode(UnsafeUtility.AddressOf(ref val), typeIndex); } @@ -1992,21 +2079,34 @@ public static int GetHashCode(void* val, TypeIndex typeIndex) public static int GetHashCode(object val, TypeIndex typeIndex) { #if !UNITY_DOTSRUNTIME - if (IsManagedComponent(typeIndex)) + if (IsManagedType(typeIndex)) { var typeInfo = GetFastEqualityTypeInfoPointer()[typeIndex.Index]; return FastEquality.ManagedGetHashCode(val, typeInfo); } else { + // IL2CPP has a bug where we can fail to detect managed types correctly which will + // cause the code #else codepath to throw if it contains managed references, so we provide + // an alternative path until that is fixed (UUM-43422) +#if ENABLE_IL2CPP var ptr = (byte*)UnsafeUtility.PinGCObjectAndGetAddress(val, out var handle) + ObjectOffset; + var result = GetHashCode(ptr, typeIndex); UnsafeUtility.ReleaseGCObject(handle); return result; +#else + var handle = GCHandle.Alloc(val, GCHandleType.Pinned); + var ptr = (byte*)handle.AddrOfPinnedObject(); + var result = GetHashCode(ptr, typeIndex); + + handle.Free(); + return result; +#endif } #else - return GetBoxedHashCode(val, typeIndex.Index); + return GetBoxedHashCode(val, typeIndex.Index); #endif } @@ -2187,10 +2287,10 @@ public static unsafe object ConstructComponentFromBuffer(TypeIndex typeIndex, vo object obj = Activator.CreateInstance(type); if (data!=null && tinfo.TypeSize != 0) { - var ptr = (byte*) UnsafeUtility.PinGCObjectAndGetAddress(obj, out var handle) + ObjectOffset; - + var handle = GCHandle.Alloc(obj, GCHandleType.Pinned); + var ptr = ((byte*)handle.AddrOfPinnedObject()); UnsafeUtility.MemCpy(ptr, data, tinfo.TypeSize); - UnsafeUtility.ReleaseGCObject(handle); + handle.Free(); } return obj; @@ -2364,7 +2464,7 @@ static void AddUnityEngineObjectTypeToListIfSupported(HashSet componentTyp { if (type.ContainsGenericParameters) return; - if (type.IsAbstract) + if (type.IsAbstract && !typeof(Component).IsAssignableFrom(type)) return; componentTypeSet.Add(type); } @@ -2409,10 +2509,6 @@ static void AddComponentTypeToListIfSupported(HashSet typeSet, Type type) static void InitializeAllComponentTypes() { -#if UNITY_EDITOR - var stopWatch = new Stopwatch(); - stopWatch.Start(); -#endif try { Profiler.BeginSample(nameof(InitializeAllComponentTypes)); @@ -2517,11 +2613,6 @@ static void InitializeAllComponentTypes() Profiler.EndSample(); } -#if UNITY_EDITOR - // Save the time since profiler might not catch the first frame. - stopWatch.Stop(); - Console.WriteLine($"TypeManager.Initialize took: {stopWatch.ElapsedMilliseconds}ms"); -#endif } private static void GatherSharedComponentMethods(Dictionary indexByType) @@ -2897,7 +2988,7 @@ private static (bool isSerializable, bool hasChunkSerializableAttribute) IsTypeV if (cache.TryGetValue(type, out result)) return result; - if (type.GetCustomAttribute() != null) + if (Attribute.IsDefined(type, typeof(ChunkSerializableAttribute))) { result = (true, true); cache.Add(type, result); @@ -3186,7 +3277,7 @@ private static void AddFastEqualityInfo(Type type, bool isUnityEngineObject = fa s_FastEqualityTypeInfoList.Add(FastEquality.CreateTypeInfo(type)); #endif } - + private struct TypeManagerKeyContext { } private struct SharedTypeInfos diff --git a/Unity.Entities/Types/TypeManagerAspects.cs b/Unity.Entities/Types/TypeManagerAspects.cs index 12bf848..d367c12 100644 --- a/Unity.Entities/Types/TypeManagerAspects.cs +++ b/Unity.Entities/Types/TypeManagerAspects.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Linq; using System.Reflection; @@ -72,4 +71,3 @@ internal static int GetAspectTypeIndex() where T : struct, IAspect } } } -#endif diff --git a/Unity.Entities/Types/TypeManagerFieldInfo.cs b/Unity.Entities/Types/TypeManagerFieldInfo.cs deleted file mode 100644 index 34d8955..0000000 --- a/Unity.Entities/Types/TypeManagerFieldInfo.cs +++ /dev/null @@ -1,104 +0,0 @@ -using System; -using System.Collections.Generic; -using Unity.Collections; -using Unity.Collections.LowLevel.Unsafe; - -namespace Unity.Entities -{ -#if UNITY_DOTSRUNTIME - /// - /// GenerateComponentFieldInfo registers which component type should have field information generated during compilation. - /// A NativeArray<FieldInfo> can be retrieved for this component (and, implicitly, its member types) via calls to - /// GetFieldInfos(typeof(SomeType)). - /// Example usage: - /// [assembly: GenerateComponentFieldInfo(typeof(MyComponent))] - /// - [AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)] - public class GenerateComponentFieldInfoAttribute : Attribute - { - public Type ComponentType; - - public GenerateComponentFieldInfoAttribute(Type type) - { - ComponentType = type; - } - } -#endif - - public static unsafe partial class TypeManager - { -#if UNITY_DOTSRUNTIME - static Dictionary s_TypeToFieldInfosMap; - static List s_FieldTypes; - static List s_FieldNames; - static NativeList s_FieldInfos; - - static void InitializeFieldInfoState() - { - s_FieldInfos = new NativeList(Allocator.Persistent); - s_FieldTypes = new List(); - s_FieldNames = new List(); - s_TypeToFieldInfosMap = new Dictionary(); - } - - static void ShutdownFieldInfoState() - { - s_TypeToFieldInfosMap.Clear(); - s_FieldNames.Clear(); - s_FieldTypes.Clear(); - s_FieldInfos.Dispose(); - } - - public struct FieldInfo - { - public FieldInfo(int offset, int fieldTypeIndex, int fieldNameIndex) - { - Offset = offset; - FieldTypeIndex = fieldTypeIndex; - FieldNameIndex = fieldNameIndex; - } - - public int Offset; - internal int FieldTypeIndex; - internal int FieldNameIndex; - - public Type FieldType => s_FieldTypes[FieldTypeIndex]; - public string FieldName => s_FieldNames[FieldNameIndex]; - } - - /// - /// Gets an array of FieldInfo for a given type, if it has field information - /// generated via the attribute. - /// - /// - /// You can use the Types from the returned NativeArray's FieldInfo element's - /// FieldType property to call this method recursively. - /// - /// The type to get FieldInfo for. - /// Returns a NativeArray of FieldInfo. - public static NativeArray GetFieldInfos(Type type) - { - if (!s_TypeToFieldInfosMap.TryGetValue(type, out var lookup)) - throw new ArgumentException($"'{type}' is not a Component type or a nested field type of a component. We only generate FieldInfo for Components and their fields if the component was registered using [GenerateComponentFieldInfo]."); - - var array = NativeArrayUnsafeUtility.ConvertExistingDataToNativeArray((FieldInfo*)s_FieldInfos.GetUnsafeReadOnlyPtr() + lookup.Index, lookup.Count, Allocator.None); - -#if ENABLE_UNITY_COLLECTIONS_CHECKS - // This handle isn't correct but collections makes this way more difficult than this needs to be - // and we know s_TypeInfos has the same lifetime and readonly requirement as the fieldinfos - var handle = NativeArrayUnsafeUtility.GetAtomicSafetyHandle(s_TypeInfos); - NativeArrayUnsafeUtility.SetAtomicSafetyHandle(ref array, handle); -#endif - return array; - } -#else - static void InitializeFieldInfoState() - { - } - - static void ShutdownFieldInfoState() - { - } -#endif - } -} diff --git a/Unity.Entities/Types/TypeManagerFieldInfo.cs.meta b/Unity.Entities/Types/TypeManagerFieldInfo.cs.meta deleted file mode 100644 index 7ab5f90..0000000 --- a/Unity.Entities/Types/TypeManagerFieldInfo.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: dad1d817564469d469d172a504fd14ac -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Entities/Types/TypeManagerSystems.cs b/Unity.Entities/Types/TypeManagerSystems.cs index 89fa9e5..0a29723 100644 --- a/Unity.Entities/Types/TypeManagerSystems.cs +++ b/Unity.Entities/Types/TypeManagerSystems.cs @@ -15,7 +15,7 @@ namespace Unity.Entities { - /// + /// /// Provides a unique id for system types as well as quick lookup information about the system type itself. /// This value is fully deterministic at runtime but should not be considered deterministic across builds /// and thus should not be serialized. @@ -257,7 +257,7 @@ static public unsafe partial class TypeManager internal enum SystemAttributeKind { UpdateBefore, - UpdateAfter, + UpdateAfter, CreateBefore, CreateAfter, DisableAutoCreation, @@ -409,9 +409,8 @@ private static void ShutdownSystemsState() { v.Dispose(); } - + s_SystemFilterTypeMap.Clear(); - s_SystemTypeFlagsList.Dispose(); s_SystemFilterFlagsList.Dispose(); @@ -508,7 +507,7 @@ internal static void InitializeAllSystemTypes() for (int i = 1; i < s_SystemCount; i++) { - AddSystemAttributesToTable(GetSystemType(i)); + AddSystemAttributesToTable(GetSystemType(i)); } } finally @@ -570,7 +569,7 @@ private static void AddSystemAttributesToTable(Type systemType) else { var objArr = systemType.GetCustomAttributes(attributeType, true); - + if (objArr.Length == 0) continue; if (attrKind == SystemAttributeKind.CreateAfter) @@ -639,7 +638,7 @@ private static void AddSystemAttributesToTable(Type systemType) if (myattr.OrderFirst) flags |= SystemAttribute.kOrderFirstFlag; if (myattr.OrderLast) flags |= SystemAttribute.kOrderLastFlag; - + list.Add(new SystemAttribute { Kind = attrKind, @@ -723,7 +722,7 @@ public static T ConstructSystem(Type systemType) where T : ComponentSystemBas /// /// Return an array of all System types available to the runtime matching the WorldSystemFilterFlags. By default, /// all systems available to the runtime is returned. Prefer GetSystemTypeIndices over this to avoid - /// unnecessary reflection. + /// unnecessary reflection. /// /// Flags the returned systems can have /// Flags the returned systems must have @@ -742,7 +741,7 @@ public static IReadOnlyList GetSystems( return ret; } - + /// /// Return an array of all System types available to the runtime matching the WorldSystemFilterFlags. By default, /// all systems available to the runtime is returned. This version avoids unnecessary reflection. @@ -766,7 +765,6 @@ public static NativeList GetSystemTypeIndices( requiredFlags &= ~WorldSystemFilterFlags.Default; LookupFlags lookupFlags = new LookupFlags() { OptionalFlags = filterFlags, RequiredFlags = requiredFlags }; - if (s_SystemFilterTypeMap.TryGetValue(lookupFlags, out var systemTypeIndices)) return systemTypeIndices; @@ -794,7 +792,7 @@ public static NativeList GetSystemTypeIndices( internal static void AddSystemTypeToTablesAfterInit(Type systemType) { Assertions.Assert.IsTrue(SharedSystemCount.Ref.UnsafeDataPointer != null, "This method must not be called until TypeManager initialization is complete."); - + if (systemType == null || s_ManagedSystemTypeToIndex.ContainsKey(systemType)) return; @@ -822,9 +820,9 @@ internal static void AddSystemTypeToTablesAfterInit(Type systemType) // Since we may have regrown our static arrays, ensure the shared statics are updated to use the new addresses InitializeSystemSharedStatics(); } - + /// - /// Sorts a list of system types based on rules defined via the + /// Sorts a list of system types based on rules defined via the 's. /// and . /// /// A List of system types to sort. @@ -871,7 +869,7 @@ public static void SortSystemTypesInCreationOrder(NativeList in var lookupDictionary = new NativeHashMap(systemElements.Length, Allocator.Temp); var lookupDictionaryPtr = (NativeHashMap*)UnsafeUtility.AddressOf(ref lookupDictionary); - + var sysElemsPtr = (UnsafeList*)UnsafeUtility.AddressOf(ref systemElements); var badSystemTypeIndices = new NativeHashSet(16, Allocator.Temp); @@ -883,26 +881,27 @@ public static void SortSystemTypesInCreationOrder(NativeList in SystemAttributeKind.CreateAfter, SystemAttributeKind.CreateBefore, badSystemTypeIndicesPtr); - + foreach (var badindex in badSystemTypeIndices) ComponentSystemSorter.WarnAboutAnySystemAttributeBadness(badindex, null); badSystemTypeIndices.Clear(); - + ComponentSystemSorter.Sort( sysElemsPtr, lookupDictionaryPtr); - + for (int i = 0; i < systemElements.Length; ++i) indices[i] = systemElements[i].SystemTypeIndex; } - + internal static void AddSystemTypeToTables(Type type, string typeName, int typeSize, long typeHash, int systemTypeFlags, WorldSystemFilterFlags filterFlags) { if (type != null && s_ManagedSystemTypeToIndex.ContainsKey(type)) return; SystemTypeIndex systemIndex = s_SystemCount++ | systemTypeFlags; + if (type != null) { s_ManagedSystemTypeToIndex.Add(type, systemIndex); @@ -938,17 +937,10 @@ internal static WorldSystemFilterFlags GetSystemFilterFlags(Type type) { var systemIndex = GetSystemTypeIndex(type).Index; var flags = s_SystemFilterFlagsList[systemIndex]; -#if !UNITY_DOTSRUNTIME - if (flags == 0) - { - flags = MakeWorldFilterFlags(type); - s_SystemFilterFlagsList[systemIndex] = flags; - } -#endif + return flags; } - - + internal static WorldSystemFilterFlags GetSystemFilterFlags(SystemTypeIndex i) { return s_SystemFilterFlagsList[i.Index]; @@ -969,7 +961,7 @@ internal static bool IsSystemTypeIndex(SystemTypeIndex systemTypeIndex) var systemTypeIndexNoFlags = systemTypeIndex & SystemTypeInfo.kClearSystemTypeFlagsMask; return systemTypeIndexNoFlags >= 0 && systemTypeIndexNoFlags < SharedSystemCount.Ref.Data; } - + internal static UnsafeText* GetSystemTypeNamesPointer() { @@ -1026,12 +1018,13 @@ public static SystemTypeIndex GetSystemTypeIndex() return index; } - + internal static SystemTypeIndex GetSystemTypeIndexNoThrow() { return SharedSystemTypeIndex.Ref.Data; } + internal static long GetSystemTypeHash(Type type) { var systemIndex = GetSystemTypeIndex(type); @@ -1058,7 +1051,7 @@ internal static int GetSystemTypeSize(SystemTypeIndex index) { return s_SystemTypeSizes[index.Index]; } - + internal static int GetSystemTypeSize() { var systemIndex = GetSystemTypeIndex(); @@ -1086,7 +1079,7 @@ internal static SystemTypeIndex GetSystemTypeIndexNoThrow(Type type) /// public static SystemTypeIndex GetSystemTypeIndex(Type type) { - int index = GetSystemTypeIndexNoThrow(type); + var index = GetSystemTypeIndexNoThrow(type); if (index == -1) { if (!typeof(ISystem).IsAssignableFrom(type) && !typeof(ComponentSystemBase).IsAssignableFrom(type)) @@ -1099,8 +1092,8 @@ public static SystemTypeIndex GetSystemTypeIndex(Type type) return ret; } } - - + + return index; } @@ -1150,16 +1143,14 @@ internal static NativeList GetSystemAttributes( SystemAttributeKind kind, Allocator allocator = Allocator.Temp) { - Assertions.Assert.IsTrue(s_Initialized, - "The TypeManager must be initialized before the TypeManager can be used."); + Assertions.Assert.IsTrue(s_Initialized, "The TypeManager must be initialized before the TypeManager can be used."); var attributesList = (UnsafeList*)SharedSystemAttributes.Ref.Data; if (IsSystemTypeIndex(systemTypeIndex)) { var list = attributesList[systemTypeIndex.Index]; - var ret = new NativeList(list.Length, allocator); - ; + var ret = new NativeList(list.Length, allocator);; for (int i = 0; i < list.Length; i++) { @@ -1171,8 +1162,7 @@ internal static NativeList GetSystemAttributes( } else { - UnityEngine.Debug.LogError( - $"System type index {systemTypeIndex} is not valid, returning empty attribute list."); + UnityEngine.Debug.LogError($"System type index {systemTypeIndex} is not valid, returning empty attribute list."); return new NativeList(0, allocator); } } @@ -1185,7 +1175,7 @@ internal static NativeList GetSystemAttributes( /// Returns all attributes of type attributeType decorating systemType public static Attribute[] GetSystemAttributes(Type systemType, Type attributeType) { - Attribute[] attributes; + Attribute[] attributes; var kDisabledCreationAttribute = typeof(DisableAutoCreationAttribute); if (attributeType == kDisabledCreationAttribute) @@ -1221,7 +1211,7 @@ public static Attribute[] GetSystemAttributes(Type systemType, Type attributeTyp } return attributes; } - + private struct SharedSystemTypeIndex { public static ref SystemTypeIndex Get(Type systemType) @@ -1229,22 +1219,22 @@ public static ref SystemTypeIndex Get(Type systemType) return ref SharedStatic.GetOrCreate(typeof(TypeManagerKeyContext), systemType).Data; } } - + private struct SharedSystemTypeNames { public static readonly SharedStatic Ref = SharedStatic.GetOrCreate(); } - + private struct SharedSystemAttributes { public static readonly SharedStatic Ref = SharedStatic.GetOrCreate(); } - + private struct SharedSystemTypeHashes { public static readonly SharedStatic Ref = SharedStatic.GetOrCreate(); } - + // Marked as internal as this is used by StaticTypeRegistryILPostProcessor internal struct SharedSystemTypeIndex { @@ -1255,7 +1245,7 @@ private struct SharedSystemCount { public static readonly SharedStatic Ref = SharedStatic.GetOrCreate(); } - + static bool FilterSystemType(SystemTypeIndex systemTypeIndex, LookupFlags lookupFlags) { var attrs = GetSystemAttributes(systemTypeIndex, SystemAttributeKind.DisableAutoCreation); @@ -1282,7 +1272,7 @@ static bool FilterSystemType(SystemTypeIndex systemTypeIndex, LookupFlags lookup return (lookupFlags.OptionalFlags & systemFlags) != 0 && (lookupFlags.RequiredFlags & systemFlags) == lookupFlags.RequiredFlags; } - + #if !UNITY_DOTSRUNTIME internal static IEnumerable GetTypesDerivedFrom(Type type) { diff --git a/Unity.Entities/World.cs b/Unity.Entities/World.cs index 410c6eb..b15b747 100644 --- a/Unity.Entities/World.cs +++ b/Unity.Entities/World.cs @@ -412,7 +412,7 @@ ComponentSystemBase AllocateSystemInternal(SystemTypeIndex type) throw new ArgumentException( "During destruction of a system you are not allowed to create more systems."); #endif - + return TypeManager.ConstructSystem(TypeManager.GetSystemType(type)); } @@ -511,7 +511,6 @@ void RemoveSystemInternal(ComponentSystemBase system) m_SystemLookup.Remove(type); foreach (var otherSystem in m_Systems) - // Equivalent to otherSystem.isSubClassOf(type) but compatible with NET_DOTS { var otherSystemType = otherSystem.GetType(); @@ -546,9 +545,9 @@ internal void CheckGetOrCreateSystem() /// does not exist in this World, it will first be created. /// /// - /// **Important:** This method creates a sync point if a system is created, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point if a system is created, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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 system type @@ -568,9 +567,9 @@ public SystemHandle GetOrCreateSystem() where T : ComponentSystemBase /// does not exist in this World, it will first be created. /// /// - /// **Important:** This method creates a sync point if a system is created, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point if a system is created, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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. /// /// **Note:** This system reference is not guaranteed to be safe to use. If the system or world is destroyed then the OnDestroy @@ -592,8 +591,8 @@ public SystemHandle GetOrCreateSystem() where T : ComponentSystemBase /// The instance of system type in this World. If the system /// does not exist in this World, it will first be created. public T GetOrCreateSystemManaged() where T : ComponentSystemBase - //sadly, we have to use reflection to account for the fact that T might not have been registered at startup. - //someday, we can ban this and avoid reflection here. + //sadly, we have to use reflection to account for the fact that T might not have been registered at startup. + //someday, we can ban this and avoid reflection here. { var idx = TypeManager.GetSystemTypeIndexNoThrow(); if (idx == SystemTypeIndex.Null) @@ -609,9 +608,9 @@ public T GetOrCreateSystemManaged() where T : ComponentSystemBase /// does not exist in this World, it will first be created. /// /// - /// **Important:** This method creates a sync point if a system is created, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point if a system is created, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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 system type @@ -623,29 +622,23 @@ public SystemHandle GetOrCreateSystem(Type type) } /// - /// Retrieve the handle for the instance of a system with a given system type index - /// from the current World. If the system does not exist in this World, it will first be created. + /// Retrieve the handle for the instance of a system of type from the current World. If the system + /// does not exist in this World, it will first be created. /// - /// - /// **Important:** This method creates a sync point if a system is created, which means that the EntityManager waits for all - /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method 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 system type index - /// The handle for the instance of system type index in this World. If the system + /// The system type + /// The handle for the instance of system type in this World. If the system /// does not exist in this World, it will first be created. - public SystemHandle GetOrCreateSystem(SystemTypeIndex typeIndex) + public SystemHandle GetOrCreateSystem(SystemTypeIndex type) { CheckGetOrCreateSystem(); - if (typeIndex.IsManaged) + if (type.IsManaged) { - var system = GetExistingSystemInternal(typeIndex); - return system == null ? CreateSystemInternal(typeIndex).SystemHandle : system.SystemHandle; + var system = GetExistingSystemInternal(type); + return system == null ? CreateSystemInternal(type).SystemHandle : system.SystemHandle; } - return Unmanaged.GetOrCreateUnmanagedSystem(typeIndex); + return Unmanaged.GetOrCreateUnmanagedSystem(type); } /// @@ -653,9 +646,9 @@ public SystemHandle GetOrCreateSystem(SystemTypeIndex typeIndex) /// does not exist in this World, it will first be created. /// /// - /// **Important:** This method creates a sync point if a system is created, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point if a system is created, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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. /// /// **Note:** This system reference is not guaranteed to be safe to use. If the system or world is destroyed then the OnDestroy @@ -684,38 +677,18 @@ public ComponentSystemBase GetOrCreateSystemManaged(Type type) } /// - /// Retrieve the instance of a system with a system type index from the current World. - /// If the system does not exist in this World, it will first be created. + /// Retrieve the instance of a system of type from the current World. If the system + /// does not exist in this World, it will first be created. /// - /// - /// **Important:** This method creates a sync point if a system is created, which means that the EntityManager waits for all - /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method 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. - /// - /// **Note:** This system reference is not guaranteed to be safe to use. If the system or world is destroyed then the OnDestroy - /// and cleanup functionality will have been called for this system. - /// - /// If possible, using is preferred, and instead of public member data, component data is recommended for - /// system level data that needs to be shared between systems or externally to them. This defines a data protocol for the - /// system which is separated from the system functionality. - /// - /// Private member data which is only used internally to the system is recommended. - /// - /// Keep in mind using a managed reference for systems - /// - encourages coupling of data and functionality - /// - couples data to the system type with no direct path to decouple - /// - does not provide lifetime or thread safety guarantees for data access - /// - does not provide lifetime or thread safety guarantees for system access through the returned managed reference - /// - /// The system type index - /// The instance of the system type with the system type index in this World. - /// If the system does not exist in this World, it will first be created. - public ComponentSystemBase GetOrCreateSystemManaged(SystemTypeIndex typeIndex) + /// The system type + /// The instance of system type in this World. If the system + /// does not exist in this World, it will first be created. + public ComponentSystemBase GetOrCreateSystemManaged(SystemTypeIndex type) { CheckGetOrCreateSystem(); - var system = GetExistingSystemInternal(typeIndex); - return system ?? CreateSystemInternal(typeIndex); + + var system = GetExistingSystemInternal(type); + return system ?? CreateSystemInternal(type); } /// @@ -724,9 +697,9 @@ public ComponentSystemBase GetOrCreateSystemManaged(SystemTypeIndex typeIndex) /// /// This can result in multiple instances of the same system in a single World, which is generally undesirable. /// - /// **Important:** This method creates a sync point, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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 system type @@ -743,9 +716,9 @@ public ComponentSystemBase GetOrCreateSystemManaged(SystemTypeIndex typeIndex) /// /// This can result in multiple instances of the same system in a single World, which is generally undesirable. /// - /// **Important:** This method creates a sync point, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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. /// /// **Note:** This system reference is not guaranteed to be safe to use. If the system or world is destroyed then the OnDestroy @@ -763,6 +736,7 @@ public ComponentSystemBase GetOrCreateSystemManaged(SystemTypeIndex typeIndex) /// - does not provide lifetime or thread safety guarantees for data access /// - does not provide lifetime or thread safety guarantees for system access through the returned managed reference /// + /// If has not been registered with the TypeManager, this method will register it before adding the system. /// /// The system type /// The new instance of system type in this World. @@ -774,7 +748,7 @@ public ComponentSystemBase GetOrCreateSystemManaged(SystemTypeIndex typeIndex) TypeManager.AddSystemTypeToTablesAfterInit(typeof(T)); idx = TypeManager.GetSystemTypeIndex(); } - + return (T)CreateSystemManaged(idx); } @@ -784,9 +758,9 @@ public ComponentSystemBase GetOrCreateSystemManaged(SystemTypeIndex typeIndex) /// /// This can result in multiple instances of the same system in a single World, which is generally undesirable. /// - /// **Important:** This method creates a sync point, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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 system type @@ -795,30 +769,20 @@ public SystemHandle CreateSystem(Type type) { return CreateSystem(TypeManager.GetSystemTypeIndex(type)); } - + /// - /// Create and return a handle to an instance of a system of with system type index - /// in this World. + /// Create and return a handle to an instance of a system of type in this World. /// - /// - /// This can result in multiple instances of the same system in a single World, which is generally undesirable. - /// - /// **Important:** This method creates a sync point, which means that the EntityManager waits for all - /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method 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 system type index - /// A handle to the new instance of system type with system type index - /// in this World. - public SystemHandle CreateSystem(SystemTypeIndex typeIndex) + /// The system type + /// A handle to the new instance of system type in this World. + public SystemHandle CreateSystem(SystemTypeIndex type) { CheckGetOrCreateSystem(); - if (typeIndex.IsManaged) - return CreateSystemInternal(typeIndex).SystemHandle; + if (type.IsManaged) + return CreateSystemInternal(type).SystemHandle; - return Unmanaged.GetOrCreateUnmanagedSystem(typeIndex); + return Unmanaged.GetOrCreateUnmanagedSystem(type); } /// @@ -827,9 +791,9 @@ public SystemHandle CreateSystem(SystemTypeIndex typeIndex) /// /// This can result in multiple instances of the same system in a single World, which is generally undesirable. /// - /// **Important:** This method creates a sync point, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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. /// /// **Note:** This system reference is not guaranteed to be safe to use. If the system or world is destroyed then the OnDestroy @@ -853,39 +817,16 @@ public ComponentSystemBase CreateSystemManaged(Type type) { return CreateSystemManaged(TypeManager.GetSystemTypeIndex(type)); } - + /// - /// Create and return an instance of a system of with system type index in this World. + /// Create and return an instance of a system of type in this World. /// - /// - /// This can result in multiple instances of the same system in a single World, which is generally undesirable. - /// - /// **Important:** This method creates a sync point, which means that the EntityManager waits for all - /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method 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. - /// - /// **Note:** This system reference is not guaranteed to be safe to use. If the system or world is destroyed then the OnDestroy - /// and cleanup functionality will have been called for this system. - /// - /// If possible, using is preferred, and instead of public member data, component data is recommended for - /// system level data that needs to be shared between systems or externally to them. This defines a data protocol for the - /// system which is separated from the system functionality. - /// - /// Private member data which is only used internally to the system is recommended. - /// - /// Keep in mind using a managed reference for systems - /// - encourages coupling of data and functionality - /// - couples data to the system type with no direct path to decouple - /// - does not provide lifetime or thread safety guarantees for data access - /// - does not provide lifetime or thread safety guarantees for system access through the returned managed reference - /// - /// The system type index - /// The new instance of system type with system type index in this World. - public ComponentSystemBase CreateSystemManaged(SystemTypeIndex typeIndex) + /// The system type + /// The new instance of system type in this World. + public ComponentSystemBase CreateSystemManaged(SystemTypeIndex type) { CheckGetOrCreateSystem(); - return CreateSystemInternal(typeIndex); + return CreateSystemInternal(type); } /// Obsolete. Use instead. @@ -900,9 +841,9 @@ public T AddSystem(T system) where T : ComponentSystemBase /// Adds an existing system instance to this World /// /// - /// **Important:** This method creates a sync point, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before adding the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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. /// /// **Note:** This system reference is not guaranteed to be safe to use. If the system or world is destroyed then the OnDestroy @@ -919,6 +860,8 @@ public T AddSystem(T system) where T : ComponentSystemBase /// - couples data to the system type with no direct path to decouple /// - does not provide lifetime or thread safety guarantees for data access /// - does not provide lifetime or thread safety guarantees for system access through the returned managed reference + /// + /// If has not been registered with the TypeManager, this method will register it before adding the system. /// /// The system type /// The existing system instance to add @@ -927,7 +870,12 @@ public T AddSystem(T system) where T : ComponentSystemBase public T AddSystemManaged(T system) where T : ComponentSystemBase { CheckGetOrCreateSystem(); - var systemTypeIndex = TypeManager.GetSystemTypeIndex(); + var systemTypeIndex = TypeManager.GetSystemTypeIndexNoThrow(); + if (systemTypeIndex <= 0) + { + TypeManager.AddSystemTypeToTablesAfterInit(typeof(T)); + systemTypeIndex = TypeManager.GetSystemTypeIndex(); + } if (GetExistingSystemInternal(systemTypeIndex) != null) throw new Exception($"Attempting to add system '{TypeManager.GetSystemName(systemTypeIndex)}' which has already been added to world '{Name}'"); @@ -991,7 +939,8 @@ public SystemHandle GetExistingSystem(SystemTypeIndex type) { CheckGetOrCreateSystem(); - if (type.IsManaged) { + if (type.IsManaged) + { var system = GetExistingSystemInternal(type); return system == null ? default : system.SystemHandle; } @@ -1059,9 +1008,9 @@ public ComponentSystemBase GetExistingSystemManaged(SystemTypeIndex type) /// Destroys one of the World's existing system instances. /// /// - /// **Important:** This method creates a sync point, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before destroying the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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 system to destroy. Must be an existing instance in this World. @@ -1089,9 +1038,9 @@ public void DestroySystem(SystemHandle sysHandle) /// Destroys one of the World's existing system instances. /// /// - /// **Important:** This method creates a sync point, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before destroying the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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 system to destroy. Must be an existing instance in this World. @@ -1233,6 +1182,8 @@ internal NativeList GetOrCreateSystemsAndLogException( { CheckGetOrCreateSystem(); + //don't enumerate twice + var sysHandlesToReturn = new NativeList(typesCount, allocator); var startIndex = sysHandlesToReturn.Length; @@ -1330,14 +1281,14 @@ internal NativeList GetOrCreateSystemsAndLogException( /// Creates systems from the list of types which aren't already created in the current world. /// /// - /// This method creates systems in the order they are passed in, and ignores + /// This function creates systems in the order they are passed in, and ignores /// and validity. /// If errors are encountered either when creating the system or when calling OnCreate, a default /// will be returned for that system. /// - /// **Important:** This method creates a sync point if any systems are created, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point if any systems are created, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the systems, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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 system types to create, in the order in which they should be created. @@ -1536,13 +1487,14 @@ public static class WorldExtensions /// /// This can result in multiple instances of the same system in a single World, which is generally undesirable. /// - /// **Important:** This method creates a sync point, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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 World /// The system type + /// Thrown if the system type has not been registered with the TypeManager. /// The new system instance's handle of system type in this World. public static SystemHandle CreateSystem(this World self) where T : unmanaged, ISystem { @@ -1565,15 +1517,16 @@ public static SystemHandle GetExistingSystem(this World self) where T : unman /// does not exist in this World, it will first be created. /// /// - /// **Important:** This method creates a sync point if a system is created, which means that the EntityManager waits for all + /// **Important:** This function creates a sync point if a system is created, which means that the EntityManager waits for all /// currently running Jobs to complete before creating the system, and no additional Jobs can start before - /// the method is finished. A sync point can cause a drop in performance because the ECS framework may not + /// 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 system type /// The World /// The instance's handle of system type in this World. If the system /// does not exist in this World, it will first be created. + /// Thrown if the system type has not been registered with the TypeManager. public static SystemHandle GetOrCreateSystem(this World self) where T : unmanaged, ISystem { return self.Unmanaged.GetOrCreateUnmanagedSystem(); diff --git a/Unity.Entities/WorldSystemFiltering.cs b/Unity.Entities/WorldSystemFiltering.cs index f0f58d7..9304863 100644 --- a/Unity.Entities/WorldSystemFiltering.cs +++ b/Unity.Entities/WorldSystemFiltering.cs @@ -102,6 +102,14 @@ public enum WorldSystemFilterFlags : uint /// Streaming = 1 << 13, /// + /// Worlds baking EntityBehaviours in motion + /// + EntityProxy = 1 << 14, + /// + /// Worlds baking EntityBehaviours in preview mode in motion + /// + EntityProxyPreview = 1 << 15, + /// /// Flag to include all system groups defined above as well as systems decorated with [DisableAutoCreation]. /// All = ~0u diff --git a/Unity.Entities/WorldUnmanaged.cs b/Unity.Entities/WorldUnmanaged.cs index 702ea6a..85ab850 100644 --- a/Unity.Entities/WorldUnmanaged.cs +++ b/Unity.Entities/WorldUnmanaged.cs @@ -404,7 +404,7 @@ internal SystemHandle GetExistingUnmanagedSystem(Type t) } return SystemHandle.Null; } - + internal SystemHandle GetExistingUnmanagedSystem(SystemTypeIndex t) { var hash = TypeManager.GetSystemTypeHash(t); @@ -584,16 +584,13 @@ private SystemHandle CreateUnmanagedSystem(SystemTypeIndex t, long typeHash, boo var systemType = TypeManager.GetSystemType(t); if (TypeManager.IsSystemManaged(systemType)) { -#if UNITY_DOTSRUNTIME - throw new ArgumentException($"The system {t} cannot contain managed fields. If you need have to store managed fields in your system, please use SystemBase instead."); -#else throw new ArgumentException($"The system {t} cannot contain managed fields. If you need have to store managed fields in your system, please use SystemBase instead. Reason: {UnsafeUtility.GetReasonForTypeNonBlittable(systemType)}"); -#endif } #endif var untypedHandle = CreateUnmanagedSystemInternal(m_EntityManager.World, TypeManager.GetSystemTypeSize(t), typeHash, t, out _, callOnCreate); + #if ENABLE_PROFILER EntitiesProfiler.OnSystemCreated(t, in untypedHandle); #endif @@ -1075,7 +1072,7 @@ internal ref WorldUnmanagedImpl GetImpl() /// update. Therefore user should not cache the world update allocator. public ref RewindableAllocator UpdateAllocator => ref GetImpl().DoubleUpdateAllocators->Allocator; - internal void ResetUpdateAllocator() + public void ResetUpdateAllocator() { GetImpl().DoubleUpdateAllocators->Update(); } @@ -1099,11 +1096,10 @@ internal SystemHandle CreateUnmanagedSystem(World self, bool callOnCreate) wh internal SystemHandle GetOrCreateUnmanagedSystem() where T : unmanaged, ISystem => GetImpl().GetOrCreateUnmanagedSystem(); - [ExcludeFromBurstCompatTesting("Uses managed World under the hood")] internal SystemHandle GetOrCreateUnmanagedSystem(SystemTypeIndex unmanagedType) => GetImpl().GetOrCreateUnmanagedSystem(unmanagedType); - + [ExcludeFromBurstCompatTesting("Uses managed World under the hood")] internal SystemHandle CreateUnmanagedSystem(SystemTypeIndex unmanagedType, bool callOnCreate) => GetImpl().CreateUnmanagedSystem(unmanagedType, callOnCreate); @@ -1183,8 +1179,7 @@ internal Type GetTypeOfSystem(SystemHandle SystemHandle) } /// - /// Return an existing instance of a system of type in this World. Prefer the version - /// that takes a SystemTypeIndex to avoid unnecessary reflection. + /// Return an existing instance of a system of type in this World. /// /// The system type /// The existing instance of system type in this World. If no such instance exists, the method returns default. @@ -1203,17 +1198,18 @@ public ref SystemState GetExistingSystemState() => ref GetImpl().GetExistingSystemState(); /// - /// Return an existing instance of a system of type in this World. + /// Return an existing instance of a system of type in this World. Prefer the version + /// that takes a SystemTypeIndex to avoid unnecessary reflection. /// /// The system type /// The existing instance of system type in this World. If no such instance exists, the method returns SystemHandle.Null. [ExcludeFromBurstCompatTesting("Takes System.Type")] public SystemHandle GetExistingUnmanagedSystem(Type type) => GetImpl().GetExistingUnmanagedSystem(type); - + /// /// Return an existing instance of a system of type in this World. This avoids - /// unnecessary reflection. + /// unnecessary reflection. /// /// The system type /// The existing instance of system type in this World. If no such instance exists, the method returns SystemHandle.Null. diff --git a/Unity.Mathematics.Extensions.Hybrid/Unity.Mathematics.Extensions.Hybrid.asmdef b/Unity.Mathematics.Extensions.Hybrid/Unity.Mathematics.Extensions.Hybrid.asmdef index 989937d..fcca204 100644 --- a/Unity.Mathematics.Extensions.Hybrid/Unity.Mathematics.Extensions.Hybrid.asmdef +++ b/Unity.Mathematics.Extensions.Hybrid/Unity.Mathematics.Extensions.Hybrid.asmdef @@ -13,9 +13,7 @@ "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [ - "!UNITY_DOTSRUNTIME" - ], + "defineConstraints": [], "versionDefines": [], "noEngineReferences": false -} \ No newline at end of file +} diff --git a/Unity.Scenes.Editor.PerformanceTests/Assets/Empty.prefab b/Unity.Scenes.Editor.PerformanceTests/Assets/Empty.prefab index 20e1526..1f70c35 100644 --- a/Unity.Scenes.Editor.PerformanceTests/Assets/Empty.prefab +++ b/Unity.Scenes.Editor.PerformanceTests/Assets/Empty.prefab @@ -26,7 +26,6 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 diff --git a/Unity.Scenes.Editor.PerformanceTests/Assets/Sphere.prefab b/Unity.Scenes.Editor.PerformanceTests/Assets/Sphere.prefab index faf8ad5..4fb5f9b 100644 --- a/Unity.Scenes.Editor.PerformanceTests/Assets/Sphere.prefab +++ b/Unity.Scenes.Editor.PerformanceTests/Assets/Sphere.prefab @@ -29,7 +29,6 @@ Transform: m_LocalRotation: {x: 0, y: 0, z: 0, w: 1} m_LocalPosition: {x: 0, y: 0, z: 0} m_LocalScale: {x: 1, y: 1, z: 1} - m_ConstrainProportionsScale: 0 m_Children: [] m_Father: {fileID: 0} m_RootOrder: 0 @@ -53,7 +52,6 @@ MeshRenderer: m_CastShadows: 1 m_ReceiveShadows: 1 m_DynamicOccludee: 1 - m_StaticShadowCaster: 0 m_MotionVectors: 1 m_LightProbeUsage: 1 m_ReflectionProbeUsage: 1 @@ -92,16 +90,8 @@ SphereCollider: m_PrefabAsset: {fileID: 0} m_GameObject: {fileID: 2448337362175982884} m_Material: {fileID: 0} - m_IncludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_ExcludeLayers: - serializedVersion: 2 - m_Bits: 0 - m_LayerOverridePriority: 0 m_IsTrigger: 0 - m_ProvidesContacts: 0 m_Enabled: 1 - serializedVersion: 3 + serializedVersion: 2 m_Radius: 0.5 m_Center: {x: 0, y: 0, z: 0} diff --git a/Unity.Scenes.Editor.PerformanceTests/LiveConversionEditorPerformanceTests.cs b/Unity.Scenes.Editor.PerformanceTests/LiveConversionEditorPerformanceTests.cs index 4907736..60c15b4 100644 --- a/Unity.Scenes.Editor.PerformanceTests/LiveConversionEditorPerformanceTests.cs +++ b/Unity.Scenes.Editor.PerformanceTests/LiveConversionEditorPerformanceTests.cs @@ -544,7 +544,7 @@ public IEnumerator LiveConversion_Performance_DeleteFromMany([Values(100, 1000)] } } - [UnityTest, Performance, Ignore("DOTS-3826")] + [UnityTest, Performance] public IEnumerator LiveConversion_Performance_CreateHierarchy([Values(1, 5, 8)]int depth, [Values] ObjectKind kind) { var subScene = m_Test.CreateEmptySubScene("TestSubScene", true); diff --git a/Unity.Scenes.Editor.Tests/Assets/TestMaterial.mat b/Unity.Scenes.Editor.Tests/Assets/TestMaterial.mat index eed7a0f..68e485d 100644 --- a/Unity.Scenes.Editor.Tests/Assets/TestMaterial.mat +++ b/Unity.Scenes.Editor.Tests/Assets/TestMaterial.mat @@ -19,8 +19,9 @@ Material: m_CustomRenderQueue: 2000 stringTagMap: RenderType: Opaque - disabledShaderPasses: [] - m_LockedProperties: + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: @@ -120,6 +121,7 @@ Material: - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} m_BuildTextureStacks: [] + m_AllowLocking: 1 --- !u!114 &4812924117557409382 MonoBehaviour: m_ObjectHideFlags: 11 @@ -130,6 +132,6 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} - m_Name: - m_EditorClassIdentifier: - version: 7 + m_Name: + m_EditorClassIdentifier: + version: 9 diff --git a/Unity.Scenes.Editor.Tests/Content/ContentDeliveryServiceTests.cs b/Unity.Scenes.Editor.Tests/Content/ContentDeliveryServiceTests.cs index 64135b0..190b7cb 100644 --- a/Unity.Scenes.Editor.Tests/Content/ContentDeliveryServiceTests.cs +++ b/Unity.Scenes.Editor.Tests/Content/ContentDeliveryServiceTests.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using NUnit.Framework; using System; using System.Collections; @@ -235,4 +234,3 @@ public IEnumerator WhenContentDeliveryCancelled_StateIsCancelled_FileDoesNotExis } } } -#endif diff --git a/Unity.Scenes.Editor.Tests/Content/ContentDownloadServiceTests.cs b/Unity.Scenes.Editor.Tests/Content/ContentDownloadServiceTests.cs index fd5491a..13e6854 100644 --- a/Unity.Scenes.Editor.Tests/Content/ContentDownloadServiceTests.cs +++ b/Unity.Scenes.Editor.Tests/Content/ContentDownloadServiceTests.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using NUnit.Framework; using System; using System.Collections; @@ -181,4 +180,3 @@ public IEnumerator UnendingDownloadOperations_CanBeCancelled() } } -#endif diff --git a/Unity.Scenes.Editor.Tests/Content/ContentLocationServiceTests.cs b/Unity.Scenes.Editor.Tests/Content/ContentLocationServiceTests.cs index 557867d..03fe576 100644 --- a/Unity.Scenes.Editor.Tests/Content/ContentLocationServiceTests.cs +++ b/Unity.Scenes.Editor.Tests/Content/ContentLocationServiceTests.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using NUnit.Framework; using Unity.Entities.Content; using Unity.Collections.LowLevel.Unsafe; @@ -82,4 +81,3 @@ public void ResolvedLocations_MatchesExpectedLocations() } } -#endif diff --git a/Unity.Scenes.Editor.Tests/LiveConversionEditorTests.cs b/Unity.Scenes.Editor.Tests/LiveConversionEditorTests.cs index 5780843..c942863 100644 --- a/Unity.Scenes.Editor.Tests/LiveConversionEditorTests.cs +++ b/Unity.Scenes.Editor.Tests/LiveConversionEditorTests.cs @@ -5,9 +5,6 @@ using System.IO; using System.Text.RegularExpressions; using NUnit.Framework; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -#endif using Unity.Collections; using Unity.Entities; using Unity.Entities.Conversion; @@ -46,7 +43,7 @@ namespace Unity.Scenes.Editor.Tests [TestFixture] // Disabled on Linux because these tests generate too many file handles: DPE-568 [UnityPlatform(exclude = new[] {RuntimePlatform.LinuxEditor})] -#if !UNITY_DOTSRUNTIME && !UNITY_WEBGL +#if !UNITY_WEBGL [ConditionalIgnore("IgnoreForCoverage", "Fails randonly when ran with code coverage enabled")] #endif abstract class LiveBakingAndConversionBase @@ -2058,8 +2055,6 @@ public IEnumerator IncrementalConversion_WithTextureDependencyInScene_DestroyAnd #if !DOTS_DISABLE_DEBUG_NAMES [UnityTest] - // Unstable on Linux: DOTS-5341 - [UnityPlatform(exclude = new[] {RuntimePlatform.LinuxEditor})] public IEnumerator IncrementalConversion_WhenGameObjectIsRenamed_TargetEntityIsRenamed([Values]Mode mode) { { @@ -3787,6 +3782,47 @@ public IEnumerator IncrementalBaking_EnabledChanged([Values]bool initiallyEnable } } } + [UnityTest] + public IEnumerator IncrementalBaking_ForceBakingOnDisabledComponents_EnabledChanged([Values]Mode mode) + { + // One Authoring, two bakers, one Forcing on not-forcing + GameObject root = null; + SubScene subScene; + { + subScene = CreateSubSceneFromObjects("TestSubScene", true, () => + { + root = new GameObject("Root"); + var authoring = root.AddComponent(); + authoring.enabled = false; + return new List {root}; + }); + } + + yield return GetEnterPlayMode(mode); + { + var w = GetLiveConversionWorld(mode); + + yield return UpdateEditorAndWorld(w); + + var bakingSystem = GetBakingSystem(w, subScene.SceneGUID); + Assert.IsNotNull(bakingSystem); + + var testForcedBakingQuery = w.EntityManager.CreateEntityQuery(new EntityQueryDesc + { + All = new ComponentType[]{typeof(TestComponentForceBakingOnDisabledAuthoring.ForcedBaking)}, + Options = EntityQueryOptions.IncludeDisabledEntities + }); + var testNotForcedBakingQuery = w.EntityManager.CreateEntityQuery(new EntityQueryDesc + { + All = new ComponentType[]{typeof(TestComponentForceBakingOnDisabledAuthoring.NotForcedBaking)}, + Options = EntityQueryOptions.IncludeDisabledEntities + }); + + Assert.AreEqual(1, testForcedBakingQuery.CalculateEntityCount(), $"Expected Baker to force baking on disabled component."); + Assert.AreEqual(0, testNotForcedBakingQuery.CalculateEntityCount(), $"Expected normal Baker to not have baked."); + + } + } [UnityTest] public IEnumerator IncrementalBaking_IsActiveAndEnabledChanged([Values]Mode mode) @@ -5850,6 +5886,143 @@ public IEnumerator IncrementalBaking_CreateParent([Values]Mode mode) } } + [UnityTest] + public IEnumerator IncrementalBaking_NewGameObjectDoesntTriggerDependOnParentTransformHierarchy([Values]Mode mode) + { + // By default TransformBaker will cause every entity to have transform usage, so for now circumvent this by only running TransformUsageBaker and nothing else. + using var baking = new BakerDataUtility.OverrideBakers(true, typeof(MockDataAuthoringBaker_TransformDependencyBaker), typeof(AddTransformUsageFlag.Baker)); + + GameObject refGo1 = null; + GameObject refGo2 = null; + GameObject rootRefGo2 = null; + Unity.Scenes.SubScene subScene; + { + subScene = CreateSubSceneFromObjects("TestSubScene", true, () => + { + // Root Object + refGo1 = new GameObject("RefGo1"); + refGo1.transform.localPosition = new float3(1f, 1f, 1f); + refGo1.AddComponent(); + + // Child Object + rootRefGo2 = new GameObject("Root RefGo2"); + rootRefGo2.transform.localPosition = new float3(1f, 1f, 1f); + + refGo2 = new GameObject("RefGo2"); + refGo2.transform.localPosition = new float3(1f, 1f, 1f); + refGo2.transform.parent = rootRefGo2.transform; + refGo2.AddComponent(); + + return new List {refGo1, rootRefGo2}; + }); + } + + yield return GetEnterPlayMode(mode); + + { + var w = GetLiveConversionWorld(Mode.Edit); + var bakingWorld = GetBakingWorld(w, subScene.SceneGUID); + var bakingSystem = GetBakingSystem(w, subScene.SceneGUID); + Assert.IsNotNull(bakingSystem); + + yield return UpdateEditorAndWorld(w); + + var refEntity = bakingSystem.GetEntity(refGo1); + Assert.AreNotEqual(Entity.Null, refEntity); + + bakingSystem.ClearDidBake(); + + // Create a new gameobject to become the root + GameObject root = new GameObject($"Root"); + + SceneManager.MoveGameObjectToScene(root, subScene.EditingScene); + + Undo.RegisterCreatedObjectUndo(root, "Creating Other Root Object"); + + var flagComponent = Undo.AddComponent(root); + flagComponent.flags = TransformUsageFlags.Dynamic; + + yield return UpdateEditorAndWorld(w); + + Assert.IsTrue(bakingSystem.DidBake(root)); + Assert.IsFalse(bakingSystem.DidBake(refGo1)); + Assert.IsFalse(bakingSystem.DidBake(refGo2)); + + // Check that moving the rootRefGo2 triggers the baker in refGo2 + ChangeLocalPosition(rootRefGo2, new float3(2f, 2f, 2f)); + + yield return UpdateEditorAndWorld(w); + + Assert.IsFalse(bakingSystem.DidBake(root)); + Assert.IsFalse(bakingSystem.DidBake(refGo1)); + Assert.IsTrue(bakingSystem.DidBake(refGo2)); + Assert.IsTrue(bakingSystem.DidBake(rootRefGo2)); + } + } + + [UnityTest] + public IEnumerator IncrementalBaking_NewGameObjectDoesntTriggerOtherTransformBakers([Values]Mode mode) + { + // By default TransformBaker will cause every entity to have transform usage, so for now circumvent this by only running TransformUsageBaker and nothing else. + using var baking = new BakerDataUtility.OverrideBakers(true, typeof(MockDataAuthoringBaker_TransformBaker), typeof(AddTransformUsageFlag.Baker)); + + GameObject refGo1 = null; + GameObject refGo2 = null; + Unity.Scenes.SubScene subScene; + { + subScene = CreateSubSceneFromObjects("TestSubScene", true, () => + { + // Root Object + refGo1 = new GameObject("RefGo1"); + refGo1.transform.localPosition = new float3(1f, 1f, 1f); + refGo1.AddComponent(); + + // Child Object + var rootRefGo2 = new GameObject("Root RefGo2"); + rootRefGo2.transform.localPosition = new float3(1f, 1f, 1f); + + refGo2 = new GameObject("RefGo2"); + refGo2.transform.localPosition = new float3(1f, 1f, 1f); + refGo2.transform.parent = rootRefGo2.transform; + refGo2.AddComponent(); + + return new List {refGo1, rootRefGo2}; + }); + } + + yield return GetEnterPlayMode(mode); + + { + var w = GetLiveConversionWorld(Mode.Edit); + var bakingWorld = GetBakingWorld(w, subScene.SceneGUID); + var bakingSystem = GetBakingSystem(w, subScene.SceneGUID); + Assert.IsNotNull(bakingSystem); + + yield return UpdateEditorAndWorld(w); + + var refEntity = bakingSystem.GetEntity(refGo1); + Assert.AreNotEqual(Entity.Null, refEntity); + + bakingSystem.ClearDidBake(); + + // Create a new gameobject to become the root + GameObject root = new GameObject($"Root"); + + SceneManager.MoveGameObjectToScene(root, subScene.EditingScene); + + Undo.RegisterCreatedObjectUndo(root, "Creating Other Root Object"); + + var flagComponent = Undo.AddComponent(root); + flagComponent.flags = TransformUsageFlags.Dynamic; + + yield return UpdateEditorAndWorld(w); + + Assert.IsTrue(bakingSystem.DidBake(root)); + Assert.IsFalse(bakingSystem.DidBake(refGo1)); + Assert.IsFalse(bakingSystem.DidBake(refGo2)); + } + } + [UnityTest] public IEnumerator IncrementalBaking_TransformUsage_HierarchyParentSwap([Values]Mode mode) { @@ -6850,6 +7023,66 @@ public IEnumerator IncrementalBaking_GetComponentsInChildren_ReverseLeafs([Value } } + [UnityTest] + public IEnumerator IncrementalBaking_GetComponent_RebakeOnReorder() + { + GameObject root; + SubScene subScene; + { + subScene = CreateEmptySubScene("TestSubScene", true); + + root = new GameObject("TestGameObject"); + root.AddComponent(); + root.AddComponent(); + root.AddComponent(); + + SceneManager.MoveGameObjectToScene(root, subScene.EditingScene); + } + + yield return GetEnterPlayMode(Mode.Edit); + { + var w = GetLiveConversionWorld(Mode.Edit); + + yield return UpdateEditorAndWorld(w); + + var bakingSystem = GetBakingSystem(w, subScene.SceneGUID); + Assert.IsNotNull(bakingSystem); + + var worldBaking = GetBakingWorld(w, subScene.SceneGUID); + + // Pre-test to check the components were added correctly + var query = new EntityQueryBuilder(Allocator.Temp).WithAll().Build(worldBaking.EntityManager); + Assert.AreEqual(1, query.CalculateEntityCount(), "Component was not correctly added to the entities"); + Assert.AreEqual(1, query.ToComponentDataArray(Allocator.Temp)[0].Value, "The wrong component is found"); + + + // Move the current 'referenced' component down + var childA = root.GetComponent(); + + Undo.RecordObject(root, "Move ChildA down (order A, B -> B, A)"); + UnityEditorInternal.ComponentUtility.MoveComponentDown(childA); + Undo.FlushUndoRecordObjects(); + + yield return UpdateEditorAndWorld(w); + + Assert.AreEqual(1, query.CalculateEntityCount(), "Component was not correctly added to the entities"); + Assert.AreEqual(2, query.ToComponentDataArray(Allocator.Temp)[0].Value, "The wrong component is found"); + + // Move the current 'un-referenced' component down + var authoringRoot = root.GetComponent(); + + Undo.RecordObject(root, "Move ChildA up (order B, A -> A, B)"); + UnityEditorInternal.ComponentUtility.MoveComponentUp(authoringRoot); + Undo.FlushUndoRecordObjects(); + + yield return UpdateEditorAndWorld(w); + + Assert.AreEqual(1, query.CalculateEntityCount(), "Component was not correctly added to the entities"); + Assert.AreEqual(1, query.ToComponentDataArray(Allocator.Temp)[0].Value, "The wrong component is found"); + + } + } + [UnityTest] public IEnumerator IncrementalBaking_GetComponent_Transform([Values]Mode mode, [Values(1,2,3)] int reparent) { @@ -7526,6 +7759,79 @@ public IEnumerator IncrementalBaking_PrimaryEntityDeletion([Values] Mode mode) } + [UnityTest] + public IEnumerator IncrementalBaking_GetAbstractComponent([Values] Mode mode) + { + using var overrideBake = new BakerDataUtility.OverrideBakers(true, typeof(Unity.Entities.Hybrid.Tests.Baking.BakerTests.GetComponent_With_AbstractBaseClass_Baker)); + GameObject go; + { + var subScene = CreateEmptySubScene("TestSubScene", true); + + go = new GameObject("Root"); + var component = go.AddComponent(); + component.Field = 5; + SceneManager.MoveGameObjectToScene(go, subScene.EditingScene); + } + yield return GetEnterPlayMode(mode); + { + var w = GetLiveConversionWorld(mode); + if (w != null) + { + var testQuery = w.EntityManager.CreateEntityQuery(ComponentType.ReadWrite()); + Assert.AreEqual(1, testQuery.CalculateEntityCount(), "The entity should have been created"); + + // Updating the abstract component should retrigger baking as it should have been recorded as a dependency to the component GetComponent_With_AbstractBaseClass + var component = go.GetComponent(); + Undo.RecordObjects(new Object[]{component}, "Changed values"); + component.Field = 7; + Undo.FlushUndoRecordObjects(); + yield return UpdateEditorAndWorld(w); + + Assert.AreEqual(1, testQuery.CalculateEntityCount()); + var entityArray = testQuery.ToEntityArray(Allocator.Temp); + Assert.AreEqual(7, w.EntityManager.GetComponentData(entityArray[0]).Field); + } + } + } + + [UnityTest] + public IEnumerator IncrementalBaking_GetAbstractComponentInParent([Values] Mode mode) + { + using var overrideBake = new BakerDataUtility.OverrideBakers(true, typeof(Unity.Entities.Hybrid.Tests.Baking.BakerTests.GetComponentInParent_With_AbstractBaseClass_Baker)); + GameObject parent; + { + var subScene = CreateEmptySubScene("TestSubScene", true); + + parent = new GameObject("Root"); + var component = parent.AddComponent(); + component.Field = 5; + GameObject child = new GameObject(); + child.transform.SetParent(parent.transform); + child.AddComponent(); + SceneManager.MoveGameObjectToScene(parent, subScene.EditingScene); + } + yield return GetEnterPlayMode(mode); + { + var w = GetLiveConversionWorld(mode); + if (w != null) + { + var testQuery = w.EntityManager.CreateEntityQuery(ComponentType.ReadWrite()); + Assert.AreEqual(1, testQuery.CalculateEntityCount(), "The entity should have been created"); + + // Updating the abstract component on the parent should retrigger baking as it should have been recorded as a dependency to the component GetComponentInParent_With_AbstractBaseClass_Baker + var component = parent.GetComponent(); + Undo.RecordObjects(new Object[]{component}, "Changed values"); + component.Field = 7; + Undo.FlushUndoRecordObjects(); + yield return UpdateEditorAndWorld(w); + + Assert.AreEqual(1, testQuery.CalculateEntityCount()); + var entityArray = testQuery.ToEntityArray(Allocator.Temp); + Assert.AreEqual(7, w.EntityManager.GetComponentData(entityArray[0]).Field); + } + } + } + [UnityTest] public IEnumerator IncrementalBaking_BakingOnlyAdditionalEntity([Values] Mode mode) { @@ -7716,6 +8022,129 @@ public IEnumerator IncrementalBaking_BakingOnlyPrimaryEntity_AdditionalOnChildre } } + public static Entity GetDestEntityFromBakingEntity(Entity bakingEntity, World bakingWorld, ref EntityQuery entityGUIDWorldQuery) + { + var bakingGUID = bakingWorld.EntityManager.GetComponentData(bakingEntity); + var entities = entityGUIDWorldQuery.ToEntityArray(Allocator.Temp); + var entityGUIDArray = entityGUIDWorldQuery.ToComponentDataArray(Allocator.Temp); + for (int index = 0; index < entityGUIDArray.Length; ++index) + { + if (entityGUIDArray[index] == bakingGUID) + { + return entities[index]; + } + } + return Entity.Null; + } + + public static void CheckLinkedEntityGroup(World world, Entity root, Entity[] legContent) + { + var buffer = world.EntityManager.GetBuffer(root).ToNativeArray(Allocator.Temp); + Assert.AreEqual(legContent.Length, buffer.Length, "LinkedEntityGroup contains a different number of entities than expected"); + + for (int index = 0; index < buffer.Length; ++index) + { + Assert.AreEqual(legContent[index], buffer[index].Value, "LinkedEntityGroup contains a different entity than expected"); + } + } + + [UnityTest] + public IEnumerator IncrementalBaking_LinkEntityGroupUpdate([Values]Mode mode) + { + GameObject root = null; + GameObject child0 = null; + SubScene subScene; + { + subScene = CreateSubSceneFromObjects("TestSubScene", true, () => + { + root = new GameObject("TestGameObject"); + var flagComponent = root.AddComponent(); + flagComponent.flags = TransformUsageFlags.Dynamic; + + child0 = new GameObject("Child0"); + flagComponent = child0.AddComponent(); + flagComponent.flags = TransformUsageFlags.Dynamic; + + child0.transform.SetParent(root.transform); + + return new List {root}; + }); + } + + yield return GetEnterPlayMode(mode); + { + var w = GetLiveConversionWorld(mode); + + yield return UpdateEditorAndWorld(w); + + var bakingSystem = GetBakingSystem(w, subScene.SceneGUID); + Assert.IsNotNull(bakingSystem); + + var worldBaking = GetBakingWorld(w, subScene.SceneGUID); + + var bakingWorldQuery = new EntityQueryBuilder(Allocator.Temp).WithAll().Build(worldBaking.EntityManager); + var destWorldQuery = new EntityQueryBuilder(Allocator.Temp).WithAll().Build(w.EntityManager); + + Assert.AreEqual(0, bakingWorldQuery.CalculateEntityCount(), "No LinkedEntityGroup expected in the baking world"); + Assert.AreEqual(0, destWorldQuery.CalculateEntityCount(), "No LinkedEntityGroup expected in the destination world"); + + // Add the linked entity group to the root + Undo.AddComponent(root); + yield return UpdateEditorAndWorld(w); + + Assert.AreEqual(1, bakingWorldQuery.CalculateEntityCount(), "1 LinkedEntityGroup expected in the baking world"); + Assert.AreEqual(1, destWorldQuery.CalculateEntityCount(), "1 LinkedEntityGroup expected in the destination world"); + + var rootBakingEntity = bakingSystem.GetEntity(root); + var child0BakingEntity = bakingSystem.GetEntity(child0); + + var entityGUIDWorldQuery = new EntityQueryBuilder(Allocator.Temp).WithAll().Build(w.EntityManager); + Entity rootEntity = GetDestEntityFromBakingEntity(rootBakingEntity, worldBaking, ref entityGUIDWorldQuery); + Entity child0Entity = GetDestEntityFromBakingEntity(child0BakingEntity, worldBaking, ref entityGUIDWorldQuery); + + // Check that the linked entity group in the baking world and the dest world contains the right entities + CheckLinkedEntityGroup(worldBaking, rootBakingEntity, new[] {rootBakingEntity, child0BakingEntity}); + CheckLinkedEntityGroup(w, rootEntity, new[] {rootEntity, child0Entity}); + + // Add an extra child to the linked entity group + SceneManager.SetActiveScene(subScene.EditingScene); + var child1 = new GameObject("Child1"); + var flagComponent = Undo.AddComponent(child1); + flagComponent.flags = TransformUsageFlags.Dynamic; + + child1.transform.SetParent(root.transform); + Undo.SetTransformParent(child1.transform, root.transform, "Set Parent"); + + Undo.RegisterCreatedObjectUndo(child1, "Create new object"); + Assert.AreEqual(child1.scene, subScene.EditingScene); + + yield return UpdateEditorAndWorld(w); + + var child1BakingEntity = bakingSystem.GetEntity(child1); + Entity child1Entity = GetDestEntityFromBakingEntity(child1BakingEntity, worldBaking, ref entityGUIDWorldQuery); + + // Check that the linked entity group in the baking world and the dest world contains the right entities + CheckLinkedEntityGroup(worldBaking, rootBakingEntity, new[] {rootBakingEntity, child0BakingEntity, child1BakingEntity}); + CheckLinkedEntityGroup(w, rootEntity, new[] {rootEntity, child0Entity, child1Entity}); + + // Remove the first child from the linked entity group + Undo.DestroyObjectImmediate(child0); + yield return UpdateEditorAndWorld(w); + + // Check that the linked entity group in the baking world and the dest world contains the right entities + CheckLinkedEntityGroup(worldBaking, rootBakingEntity, new[] {rootBakingEntity, child1BakingEntity}); + CheckLinkedEntityGroup(w, rootEntity, new[] {rootEntity, child1Entity}); + + // Remove the linked entity group from the root + Undo.DestroyObjectImmediate(root.GetComponent()); + + yield return UpdateEditorAndWorld(w); + + Assert.AreEqual(0, bakingWorldQuery.CalculateEntityCount(), "No LinkedEntityGroup expected in the baking world"); + Assert.AreEqual(0, destWorldQuery.CalculateEntityCount(), "No LinkedEntityGroup expected in the destination world"); + } + } + Entities.Hash128 CalculateBlobHash(int value) => CustomHashHelpers.Compute(value); [UnityTest] diff --git a/Unity.Scenes.Editor.Tests/SerializeUtilityDeterminism.cs b/Unity.Scenes.Editor.Tests/SerializeUtilityDeterminism.cs deleted file mode 100644 index e6a37c5..0000000 --- a/Unity.Scenes.Editor.Tests/SerializeUtilityDeterminism.cs +++ /dev/null @@ -1,94 +0,0 @@ -using System.IO; -using NUnit.Framework; -using Unity.Entities; -using Unity.Entities.Serialization; -using Unity.Entities.Tests; -using Unity.Collections.NotBurstCompatible; - -namespace Unity.Scenes.Tests -{ - public class SerializeUtilityDeterminism - { - public enum Mode - { - Yaml, - YamlDumpChunk, - Binary - } - static byte[] Write(EntityManager manager, Mode mode) - { - if (mode == Mode.Binary) - { - using (var writer = new TestBinaryWriter(manager.World.UpdateAllocator.ToAllocator)) - { - SerializeUtility.SerializeWorld(manager, writer); - return writer.content.ToArrayNBC(); - } - } - else - { - // Save the World to a memory buffer via a a Stream Writer - using (var memStream = new MemoryStream()) - using (var sw = new StreamWriter(memStream)) - { - sw.NewLine = "\n"; - if (mode == Mode.Yaml) - SerializeUtility.SerializeWorldIntoYAML(manager, sw, false); - else if (mode == Mode.YamlDumpChunk) - SerializeUtility.SerializeWorldIntoYAML(manager, sw, true); - - sw.Flush(); - memStream.Seek(0, SeekOrigin.Begin); - return memStream.ToArray(); - } - } - } - - [Test] - public void DeterministicWrite([Values]Mode mode, [Values(0, 20)]int addBuffercount, [Values(1, 20)]int size) - { - using (var world0 = new World("1")) - using (var world1 = new World("2")) - { - FillBuffer(addBuffercount, size, 100, world0); - FillIComponentData(10, world0); - var result0= Write(world0.EntityManager, mode); - - FillBuffer(addBuffercount, size, 200, world1); - FillIComponentData(10, world1); - var result1= Write(world1.EntityManager, mode); - - // Enable for debugging purposes (Diff these two files to see what is indeterministic if the test fails) - //File.WriteAllBytes("test-src.txt", result0); - //File.WriteAllBytes("test-dst.txt", result1); - - Assert.AreEqual(result0, result1); - } - } - - // Fill buffer with interministic values then resize and fill with deterministic values - // Making sure that we don't write buffer data that is no longer in use. - static void FillBuffer(int addBufferCount, int size, int fillvalue, World world) - { - var entity = world.EntityManager.CreateEntity(); - var buffer = world.EntityManager.AddBuffer(entity); - for (int i = 0; i != addBufferCount; i++) - buffer.Add(fillvalue); - buffer.ResizeUninitialized(size); - for (int i = 0; i != size; i++) - buffer[i] = i; - } - - // Create entity with indeterministic value and destroy it again - // Making sure that we don't write chunk data that has been released / not yet been cleared - static void FillIComponentData(int fillvalue, World world0) - { - var entity = world0.EntityManager.CreateEntity(); - world0.EntityManager.AddComponentData(entity, new EcsTestData(10)); - - var fillEntity = world0.EntityManager.CreateEntity(); - world0.EntityManager.AddComponentData(entity, new EcsTestData(fillvalue)); - world0.EntityManager.DestroyEntity(fillEntity); - } - } -} diff --git a/Unity.Scenes.Editor.Tests/SerializeUtilityDeterminism.cs.meta b/Unity.Scenes.Editor.Tests/SerializeUtilityDeterminism.cs.meta deleted file mode 100644 index 33a0d82..0000000 --- a/Unity.Scenes.Editor.Tests/SerializeUtilityDeterminism.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 1d8ac091f20a42cd9ad288e52b1a95aa -timeCreated: 1603651075 \ No newline at end of file diff --git a/Unity.Scenes.Editor.Tests/SubSceneConversionTests.cs b/Unity.Scenes.Editor.Tests/SubSceneConversionTests.cs index 1e6d639..e2ad36c 100644 --- a/Unity.Scenes.Editor.Tests/SubSceneConversionTests.cs +++ b/Unity.Scenes.Editor.Tests/SubSceneConversionTests.cs @@ -262,7 +262,7 @@ public void SubScene_WithoutContents_DeletingSceneAssetUnloadsScene([Values]Test } } -#if !NET_DOTS && !UNITY_DISABLE_MANAGED_COMPONENTS +#if !UNITY_DISABLE_MANAGED_COMPONENTS [Test] public void SubScene_WithNullAsset_ImportsAndLoads([Values]TestWorldSetup.TestWorldSystemFilterFlags testFilterFlags) => SubScene_WithAsset_ImportsAndLoads(null, testFilterFlags); @@ -459,7 +459,7 @@ public void SubScene_WithSharedComponent_ImportsAndLoads([Values]TestWorldSetup. } } -#if !NET_DOTS && !UNITY_DISABLE_MANAGED_COMPONENTS +#if !UNITY_DISABLE_MANAGED_COMPONENTS [Test] public void SubScene_WithComplexComponents_ImportsAndLoads([Values]TestWorldSetup.TestWorldSystemFilterFlags testFilterFlags) { diff --git a/Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs b/Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs index 9405588..87de894 100644 --- a/Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs +++ b/Unity.Scenes.Editor.Tests/SubSceneDeduplicationTests.cs @@ -4,11 +4,6 @@ using System.IO; using System.Linq; using NUnit.Framework; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -using Unity.Build.Classic; -using Unity.Build.Common; -#endif using Unity.Entities; using Unity.Entities.Hybrid.Tests; using Unity.Entities.Tests; diff --git a/Unity.Scenes.Editor/Build/RemoteContentCatalogBuildUtility.cs b/Unity.Scenes.Editor/Build/RemoteContentCatalogBuildUtility.cs index b2328d2..c879bfd 100644 --- a/Unity.Scenes.Editor/Build/RemoteContentCatalogBuildUtility.cs +++ b/Unity.Scenes.Editor/Build/RemoteContentCatalogBuildUtility.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Collections; using System.Collections.Generic; @@ -12,12 +11,6 @@ using System.Linq; using Unity.Entities.Build; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -using Unity.Build.Common; -using Unity.Build.Classic; -#endif - namespace Unity.Entities.Content { /// @@ -28,78 +21,40 @@ public static class RemoteContentCatalogBuildUtility [MenuItem("Assets/Publish/Existing Build")] static void ExistingBuildMenuItem() { -#if USING_PLATFORMS_PACKAGE - if (Selection.assetGUIDs.Length == 1 && AssetDatabase.GetMainAssetTypeAtPath(AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0])) == typeof(BuildConfiguration)) - { - var buildConfigPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]); - var buildConfig = AssetDatabase.LoadAssetAtPath(buildConfigPath); - var buildConfigName = Path.GetFileNameWithoutExtension(buildConfigPath); - var buildFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", $"{buildConfigName}/{buildConfig.GetComponent().ProductName}_Data/StreamingAssets"); - var publishFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", $"{buildConfigName}-RemoteContent"); - PublishContent(buildFolder, publishFolder, f => new string[] { "all" }); - } - else -#endif + var buildFolder = EditorUtility.OpenFolderPanel("Select Build To Publish", Path.GetDirectoryName(Application.dataPath), "Builds"); + if (!string.IsNullOrEmpty(buildFolder)) { - var buildFolder = EditorUtility.OpenFolderPanel("Select Build To Publish", Path.GetDirectoryName(Application.dataPath), "Builds"); - if (!string.IsNullOrEmpty(buildFolder)) - { - var streamingAssetsPath = $"{buildFolder}/{PlayerSettings.productName}_Data/StreamingAssets"; - PublishContent(streamingAssetsPath, $"{buildFolder}-RemoteContent", f => new string[] { "all" }); - } + var streamingAssetsPath = $"{buildFolder}/{PlayerSettings.productName}_Data/StreamingAssets"; + PublishContent(streamingAssetsPath, $"{buildFolder}-RemoteContent", f => new string[] { "all" }); } } [MenuItem("Assets/Publish/Content Update")] static void ContentUpdateMenuItem() { -#if USING_PLATFORMS_PACKAGE - if (Selection.assetGUIDs.Length == 1 && AssetDatabase.GetMainAssetTypeAtPath(AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0])) == typeof(BuildConfiguration)) - { - var subSceneGuids = new HashSet(); - var buildConfigPath = AssetDatabase.GUIDToAssetPath(Selection.assetGUIDs[0]); - var buildConfigName = Path.GetFileNameWithoutExtension(buildConfigPath); - var buildConfig = AssetDatabase.LoadAssetAtPath(buildConfigPath); - var rootSceneInfos = buildConfig.GetComponent().GetSceneInfosForBuild(); - foreach (var s in rootSceneInfos) - foreach (var h in EditorEntityScenes.GetSubScenes(s.Scene.assetGUID)) - subSceneGuids.Add(h); - var platform = buildConfig.GetComponent().Platform; - var buildTarget = platform.GetBuildTarget(); - var buildFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", $"{buildConfigName}-ContentUpdate"); - var id = GlobalObjectId.GetGlobalObjectIdSlow(BuildConfiguration.GetActive()); - BuildContent(subSceneGuids, id.assetGUID, buildTarget, buildFolder); - - var publishFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", $"{buildConfigName}-RemoteContent"); - PublishContent(buildFolder, publishFolder, f => new string[] { "all" }); - } - else -#endif + var buildFolder = EditorUtility.OpenFolderPanel("Select Build To Publish", Path.GetDirectoryName(Application.dataPath), "Builds"); + if (!string.IsNullOrEmpty(buildFolder)) { - var buildFolder = EditorUtility.OpenFolderPanel("Select Build To Publish", Path.GetDirectoryName(Application.dataPath), "Builds"); - if (!string.IsNullOrEmpty(buildFolder)) - { - var buildTarget = EditorUserBuildSettings.activeBuildTarget; - var tmpBuildFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), $"/Library/ContentUpdateBuildDir/{PlayerSettings.productName}"); + var buildTarget = EditorUserBuildSettings.activeBuildTarget; + var tmpBuildFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), $"/Library/ContentUpdateBuildDir/{PlayerSettings.productName}"); - var instance = DotsGlobalSettings.Instance; - var playerGuid = instance.GetPlayerType() == DotsGlobalSettings.PlayerType.Client ? instance.GetClientGUID() : instance.GetServerGUID(); - if (!playerGuid.IsValid) - throw new Exception("Invalid Player GUID"); + var instance = DotsGlobalSettings.Instance; + var playerGuid = instance.GetPlayerType() == DotsGlobalSettings.PlayerType.Client ? instance.GetClientGUID() : instance.GetServerGUID(); + if (!playerGuid.IsValid) + throw new Exception("Invalid Player GUID"); - var subSceneGuids = new HashSet(); - for (int i = 0; i < EditorBuildSettings.scenes.Length; i++) - { - var ssGuids = EditorEntityScenes.GetSubScenes(EditorBuildSettings.scenes[i].guid); - foreach (var ss in ssGuids) - subSceneGuids.Add(ss); - } + var subSceneGuids = new HashSet(); + for (int i = 0; i < EditorBuildSettings.scenes.Length; i++) + { + var ssGuids = EditorEntityScenes.GetSubScenes(EditorBuildSettings.scenes[i].guid); + foreach (var ss in ssGuids) + subSceneGuids.Add(ss); + } - BuildContent(subSceneGuids, playerGuid, buildTarget, tmpBuildFolder); + BuildContent(subSceneGuids, playerGuid, buildTarget, tmpBuildFolder); - var publishFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", $"{buildFolder}-RemoteContent"); - PublishContent(tmpBuildFolder, publishFolder, f => new string[] { "all" }); - } + var publishFolder = Path.Combine(Path.GetDirectoryName(Application.dataPath), "Builds", $"{buildFolder}-RemoteContent"); + PublishContent(tmpBuildFolder, publishFolder, f => new string[] { "all" }); } } @@ -252,4 +207,3 @@ private static void CreateRemoteContentLocationData(string path, int locationCou } } } -#endif diff --git a/Unity.Scenes.Editor/EditorEntityScenes.cs b/Unity.Scenes.Editor/EditorEntityScenes.cs index 3ede307..462fa38 100644 --- a/Unity.Scenes.Editor/EditorEntityScenes.cs +++ b/Unity.Scenes.Editor/EditorEntityScenes.cs @@ -2,9 +2,6 @@ using System.Collections.Generic; using System.IO; using System.Linq; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -#endif using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Core.Compression; @@ -27,10 +24,6 @@ namespace Unity.Scenes.Editor internal struct WriteEntitySceneSettings { #pragma warning disable CS0649 // Field is never assigned to, and will always have its default value - public bool IsDotsRuntime; -#if USING_PLATFORMS_PACKAGE - public BuildAssemblyCache BuildAssemblyCache; -#endif public string OutputPath; public Codec Codec; public Entity PrefabRoot; @@ -90,9 +83,6 @@ internal static SceneSectionData[] BakeAndWriteEntityScene(Scene scene, BakingSe var sections = WriteEntitySceneInternalBaking(world.EntityManager, settings.SceneGUID, scene.name, settings.AssetImportContext, sectionRefObjs, writeEntitySettings); - if (writeEntitySettings.IsDotsRuntime && sectionRefObjs.Count != 0) - throw new ArgumentException("We are serializing a world that contains UnityEngine.Object references which are not supported in Dots Runtime."); - if (disposeBlobAssetCache) settings.BlobAssetStore.Dispose(); @@ -171,17 +161,6 @@ internal static void WriteExportedTypes(AssetImportContext ctx, WriteEntityScene writer.WriteLine($"::Exported Types (by stable hash)::"); foreach (var typeInfo in typeInfos) { - // For dots runtime only, check if the build assembly cache containing all types from root asmdef, contains the typeinfo. If not throw an exception, the runtime will fail to recognize the type (probably a missing asmdef reference) - #if USING_PLATFORMS_PACKAGE - if (writeEntitySceneSettings.IsDotsRuntime && writeEntitySceneSettings.BuildAssemblyCache != null) - { - var type =typeInfo.Type; - if (!writeEntitySceneSettings.BuildAssemblyCache.HasType(type)) - throw 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."); - } - #endif - // Record exported types in a separate log file for debug purposes writer.WriteLine($"0x{typeInfo.StableTypeHash:x16} - {typeInfo.StableTypeHash,22} - {typeInfo.Type.FullName}"); } @@ -226,7 +205,9 @@ static unsafe AABB GetBoundsAndRemove(EntityManager entityManager, EntityQuery q { foreach (var e in entities) { - if (ecs->GetChunk(e)->Archetype == pEmptyArchetype) + var chunk = ecs->GetChunk(e); + var chunkArchetype = ecs->GetArchetype(chunk); + if (chunkArchetype == pEmptyArchetype) { entityManager.DestroyEntity(e); } @@ -300,8 +281,6 @@ static SceneSectionData[] WriteEntitySceneInternal(EntityManager entityManager, var subSectionList = new List(); GetSceneSections(entityManager, sceneGUID, ref subSectionList); - var extRefInfoEntities = new NativeArray(subSectionList.Count, Allocator.Temp); - NativeArray entitiesInMainSection; var sectionQuery = entityManager.CreateEntityQuery( @@ -333,19 +312,6 @@ static SceneSectionData[] WriteEntitySceneInternal(EntityManager entityManager, entitiesInMainSection = sectionQuery.ToEntityArray(Allocator.TempJob); // Each section will be serialized in its own world, entities that don't have a section are part of the main scene. - // An entity that holds the array of external references to the main scene is required for each section. - // We need to create them all before we start moving entities to section scenes, - // otherwise they would reuse entities that have been moved and mess up the remapping tables. - for (int sectionIndex = 1; sectionIndex < subSectionList.Count; ++sectionIndex) - { - if (subSectionList[sectionIndex].Section == 0) - // Main section, the only one that doesn't need an external ref array - continue; - - var extRefInfoEntity = entityManager.CreateEntity(); - entityManager.AddSharedComponentManaged(extRefInfoEntity, subSectionList[sectionIndex]); - extRefInfoEntities[sectionIndex] = extRefInfoEntity; - } // Public references array, only on the main section. var refInfoEntity = entityManager.CreateEntity(); @@ -413,18 +379,6 @@ static SceneSectionData[] WriteEntitySceneInternal(EntityManager entityManager, if (entitiesInSection.Length > 0) { - // Fetch back the external reference entity we created earlier to not disturb the mapping - var refInfoEntity = extRefInfoEntities[subSectionIndex]; - entityManager.AddBuffer(refInfoEntity); - var externRefs = entityManager.GetBuffer(refInfoEntity); - - // Store the mapping to everything in the main section - //@TODO maybe we don't need all that? is this worth worrying about? - for (int i = 0; i < entitiesInMainSection.Length; ++i) - { - ExternalEntityRef.Add(ref externRefs, new ExternalEntityRef {entityIndex = i}); - } - var entityRemapping = entityManager.CreateEntityRemapArray(Allocator.TempJob); // Entities will be remapped to a contiguous range in the section world, but they will @@ -433,13 +387,6 @@ static SceneSectionData[] WriteEntitySceneInternal(EntityManager entityManager, // size at the end of the remapping table. So we use that range for external references. var externEntityIndexStart = entityRemapping.Length - entitiesInMainSection.Length; - entityManager.AddComponentData(refInfoEntity, - new ExternalEntityRefInfo - { - SceneGUID = sceneGUID, - EntityIndexStart = externEntityIndexStart - }); - var sectionWorld = new World("SectionWorld"); var sectionManager = sectionWorld.EntityManager; @@ -468,16 +415,6 @@ static SceneSectionData[] WriteEntitySceneInternal(EntityManager entityManager, var oldExternEntityIndexStart = externEntityIndexStart; externEntityIndexStart = highestEntityIndexInUse + 1; - sectionManager.SetComponentData - ( - EntityRemapUtility.RemapEntity(ref entityRemapping, refInfoEntity), - new ExternalEntityRefInfo - { - SceneGUID = sceneGUID, - EntityIndexStart = externEntityIndexStart - } - ); - // When writing the scene, references to missing entities are set to Entity.Null by default // (but only if they have been used, otherwise they remain untouched) // We obviously don't want that to happen to our external references, so we add explicit mapping @@ -688,16 +625,14 @@ private static unsafe (int decompressedSize, int compressedSize, BlobAssetRefere using (var writer = new StreamBinaryWriter(entitiesBinaryPath)) using (var entitiesWriter = new MemoryBinaryWriter()) { - var isDotsRuntime = writeEntitySceneSettings.IsDotsRuntime; var entityRemapInfosCreated = entityRemapInfos.IsCreated; if(!entityRemapInfosCreated) entityRemapInfos = new NativeArray(scene.EntityCapacity, Allocator.Temp); blobHeader = SerializeUtility.SerializeWorldInternal(scene, entitiesWriter, out var referencedObjects, - entityRemapInfos, weakAssetRefs, serializeSetting, isDotsRuntime, buildBlobHeader); + entityRemapInfos, weakAssetRefs, serializeSetting, buildBlobHeader); - if(!isDotsRuntime) - SerializeUtilityHybrid.SerializeObjectReferences((UnityEngine.Object[])referencedObjects, out objRefs); + SerializeUtilityHybrid.SerializeObjectReferences((UnityEngine.Object[])referencedObjects, out objRefs); if (!entityRemapInfosCreated) entityRemapInfos.Dispose(); diff --git a/Unity.Scenes.Editor/EditorSubSceneLiveConversionSystem.cs b/Unity.Scenes.Editor/EditorSubSceneLiveConversionSystem.cs index e22c221..61d0975 100644 --- a/Unity.Scenes.Editor/EditorSubSceneLiveConversionSystem.cs +++ b/Unity.Scenes.Editor/EditorSubSceneLiveConversionSystem.cs @@ -24,8 +24,6 @@ partial class EditorSubSceneLiveConversionSystem : SystemBase NativeList _UnloadScenes; NativeList _LoadScenes; - ulong m_GizmoSceneCullingMask = 1UL << 59; - System.Diagnostics.Stopwatch m_Watch; internal double MillisecondsTakenByUpdate { get; set; } @@ -101,25 +99,42 @@ protected override void OnUpdate() if (_EditorLiveConversion.HasLoadedScenes()) { - // Configure scene culling masks so that game objects & entities are rendered exlusively to each other + // Configure scene culling masks so that gameobjects & entities are rendered exlusively to each other for (int i = 0; i != EditorSceneManager.sceneCount; i++) { var scene = EditorSceneManager.GetSceneAt(i); - //this is to avoid trying to get the guid of a scene loaded from a content archive during play mode - if (!scene.path.StartsWith("Assets/", System.StringComparison.OrdinalIgnoreCase) && - !scene.path.StartsWith("Packages/", System.StringComparison.OrdinalIgnoreCase)) + // This is to avoid trying to get the guid of a scene loaded from a content archive during play mode + var scenepath = scene.path; + if (!scenepath.StartsWith("Assets/", System.StringComparison.OrdinalIgnoreCase) && + !scenepath.StartsWith("Packages/", System.StringComparison.OrdinalIgnoreCase)) continue; - var sceneGUID = AssetDatabaseCompatibility.PathToGUID(scene.path); + var sceneGUID = AssetDatabaseCompatibility.PathToGUID(scenepath); if (_EditorLiveConversion.HasScene(sceneGUID)) { - if (LiveConversionEditorSettings.LiveConversionMode == LiveConversionMode.SceneViewShowsAuthoring) - EditorSceneManager.SetSceneCullingMask(scene, SceneCullingMasks.MainStageSceneViewObjects); - else if (LiveConversionEditorSettings.LiveConversionMode == LiveConversionMode.SceneViewShowsRuntime) - EditorSceneManager.SetSceneCullingMask(scene, m_GizmoSceneCullingMask); - else - EditorSceneManager.SetSceneCullingMask(scene, EditorSceneManager.DefaultSceneCullingMask); + switch (LiveConversionEditorSettings.LiveConversionMode) + { + case LiveConversionMode.SceneViewShowsAuthoring: + // Render gameobjects in SceneView but hide them in GameView + EditorSceneManager.SetSceneCullingMask(scene, SceneCullingMasks.MainStageSceneViewObjects); + break; + + case LiveConversionMode.SceneViewShowsRuntime: + // Hide gameobjects in SceneView and GameView + EditorSceneManager.SetSceneCullingMask(scene, 0); + break; + + case LiveConversionMode.Disabled: + case LiveConversionMode.LiveConvertStandalonePlayer: + // Render gameobjects in SceneView and GameView + EditorSceneManager.SetSceneCullingMask(scene, EditorSceneManager.DefaultSceneCullingMask); + break; + + default: + Debug.LogError("Missing handling of: " + LiveConversionEditorSettings.LiveConversionMode); + break; + } } } } @@ -133,9 +148,6 @@ protected override void OnUpdate() protected override void OnCreate() { - Camera.onPreCull += OnPreCull; - RenderPipelineManager.beginContextRendering += OnPreCull; - SceneView.duringSceneGui += SceneViewOnBeforeSceneGui; m_Watch = new Stopwatch(); _SceneChangeTracker = new LiveConversionSceneChangeTracker(EntityManager); @@ -148,10 +160,6 @@ protected override void OnCreate() protected override void OnDestroy() { - Camera.onPreCull -= OnPreCull; - RenderPipelineManager.beginContextRendering -= OnPreCull; - SceneView.duringSceneGui -= SceneViewOnBeforeSceneGui; - if (_EditorLiveConversion != null) _EditorLiveConversion.Dispose(); _SceneChangeTracker.Dispose(); @@ -159,49 +167,5 @@ protected override void OnDestroy() _UnloadScenes.Dispose(); _LoadScenes.Dispose(); } - - //@TODO: - // * This is a gross hack to show the Transform gizmo even though the game objects used for editing are hidden and thus the tool gizmo is not shown - // * Also we are not rendering the selection (Selection must be drawn around live linked object, not the editing object) - void SceneViewOnBeforeSceneGui(SceneView sceneView) - { - if (LiveConversionEditorSettings.LiveConversionMode == LiveConversionMode.SceneViewShowsRuntime) - { - Camera camera = sceneView.camera; - bool sceneViewIsRenderingCustomScene = camera.scene.IsValid(); - if (!sceneViewIsRenderingCustomScene) - { - // Add our gizmo hack bit before gizmo rendering so the SubScene GameObjects are considered visible - ulong newmask = camera.overrideSceneCullingMask | m_GizmoSceneCullingMask; - camera.overrideSceneCullingMask = newmask; - } - } - } - - void OnPreCull(ScriptableRenderContext src, Camera[] cameras) - { - foreach (Camera camera in cameras) - { - OnPreCull(camera); - } - } - - void OnPreCull(ScriptableRenderContext src, List cameras) - { - foreach (Camera camera in cameras) - { - OnPreCull(camera); - } - } - - void OnPreCull(Camera camera) - { - if (camera.cameraType == CameraType.SceneView) - { - // Ensure to remove our gizmo hack bit before rendering - ulong newmask = camera.overrideSceneCullingMask & ~m_GizmoSceneCullingMask; - camera.overrideSceneCullingMask = newmask; - } - } } } diff --git a/Unity.Scenes.Editor/EntitySceneBuildPlayerProcessor.cs b/Unity.Scenes.Editor/EntitySceneBuildPlayerProcessor.cs index 878bdce..5c8ba63 100644 --- a/Unity.Scenes.Editor/EntitySceneBuildPlayerProcessor.cs +++ b/Unity.Scenes.Editor/EntitySceneBuildPlayerProcessor.cs @@ -7,12 +7,9 @@ using UnityEditor.Experimental; using System.IO; using Unity.Entities.Build; -using UnityEditor.Build.Content; using UnityEngine; -#if USING_PLATFORMS_PACKAGE -using Unity.Build.Classic.Private; -#endif using UnityEditor.SceneManagement; +using UnityEngine.SceneManagement; namespace Unity.Scenes.Editor { @@ -108,12 +105,48 @@ static void LogMissingServerProvider() $"No available DotsPlayerSettingsProvider for the current platform ({EditorUserBuildSettings.activeBuildTarget}). Using the client settings instead."); } + // This method is mostly doing the same as the engine method BuildPlayer::SaveScenesBeforeBuildIfNeeded except that it saves all unsaved opened scenes as content management requires it before building + void SaveScenesBeforeBuildIfNeeded(string [] scenesToBuild) + { + if (Application.isBatchMode || EditorPrefs.GetBool("SaveScenesBeforeBuilding")) + { + // In batch mode we cannot show a dialog asking to save changes. + // Auto-save any scene even if they are not part of the build as content management requires it. + var scenesToSave = new List(); + for (int i = 0; i < SceneManager.sceneCount; i++) + { + var scene = SceneManager.GetSceneAt(i); + if (scene.isDirty) + { + scenesToSave.Add(scene); + } + } + + if (scenesToSave.Count > 0) + EditorSceneManager.SaveScenes(scenesToSave.ToArray()); + } + else + { + if (scenesToBuild.Length == 0) + { + // If we have no scenes in the build, we will build the currently active scenes. + // In that case, if we have an untitled scene, always mark it as dirty so we can ask the user to save it + for (int i = 0; i < SceneManager.sceneCount; i++) + { + var scene = SceneManager.GetSceneAt(i); + if (String.IsNullOrEmpty(scene.path)) + EditorSceneManager.MarkSceneDirty(scene); + } + } + + EditorSceneManager.SaveCurrentModifiedScenesIfUserWantsTo(); + } + } + public override void PrepareForBuild(BuildPlayerContext buildPlayerContext) { -#if USING_PLATFORMS_PACKAGE - if (BuildPlayerStep.BuildFromBuildConfiguration) - return; -#endif + // Content pipeline requires the opened scenes in the Editor to be saved to be able to build or it will fail with the exception ContentCatalogBuildUtility.BuildContentArchives failed with status 'UnsavedChanges'. + SaveScenesBeforeBuildIfNeeded(buildPlayerContext.BuildPlayerOptions.scenes); // For WebGL, we need to prepare a manifest list of all generated data files under StreamingAssets/ // that need to be synchronously accessible. diff --git a/Unity.Scenes.Editor/EntitySceneBuildUtility.cs b/Unity.Scenes.Editor/EntitySceneBuildUtility.cs index e249655..48fc7a0 100644 --- a/Unity.Scenes.Editor/EntitySceneBuildUtility.cs +++ b/Unity.Scenes.Editor/EntitySceneBuildUtility.cs @@ -6,9 +6,6 @@ using Unity.Collections; using Unity.Collections.NotBurstCompatible; using Unity.Entities; -#if USING_PLATFORMS_PACKAGE -using Unity.Build.Common; -#endif using Unity.Entities.Conversion; using Unity.Entities.Serialization; using UnityEditor; diff --git a/Unity.Scenes.Editor/EntitySectionBundlesBuildCustomizer.cs b/Unity.Scenes.Editor/EntitySectionBundlesBuildCustomizer.cs deleted file mode 100644 index 2fc5945..0000000 --- a/Unity.Scenes.Editor/EntitySectionBundlesBuildCustomizer.cs +++ /dev/null @@ -1,46 +0,0 @@ -#if USING_PLATFORMS_PACKAGE -using System; -using System.Collections.Generic; -using System.IO; -using Unity.Build.Classic; -using Unity.Entities; -using UnityEditor; -using UnityEditor.Experimental; - -namespace Unity.Scenes.Editor -{ - class EntitySectionBundlesBuildCustomizer : ClassicBuildPipelineCustomizer - { - private Action m_RegisterAdditionalFilesToDeploy; - public override void RegisterAdditionalFilesToDeploy(Action registerAdditionalFileToDeploy) - { - if (!Context.HasValue()) - return; - - var binaryFiles = Context.GetValue(); - - // Additional pre-checks to detect when the same SceneGUID or artifacts are added multiple times which will fail the builder further on. -#if false - if (binaryFiles.SceneGUID.Distinct().Count() != binaryFiles.SceneGUID.Count()) - throw new ArgumentException("Some of the EntityScenes guids in build are not unique"); - if (binaryFiles.ArtifactKeys.Distinct().Count() != binaryFiles.ArtifactKeys.Count()) - throw new ArgumentException("Some of the EntityScenes target resolved guids in build are not unique"); -#endif - - if (BuildTarget != EditorUserBuildSettings.activeBuildTarget) - throw new InvalidOperationException($"ActiveBuildTarget must be switched before the {nameof(EntitySceneBuildUtility)} runs."); - - m_RegisterAdditionalFilesToDeploy = registerAdditionalFileToDeploy; - - var buildTargetGUID = new Hash128(Context.BuildConfigurationAssetGUID); - EntitySceneBuildUtility.PrepareAdditionalFiles(buildTargetGUID, binaryFiles.SceneGUIDs.ToArray(), binaryFiles.ArtifactKeys.ToArray(), BuildTarget, InternalRegisterAdditionalFilesToDeploy); - } - - private void InternalRegisterAdditionalFilesToDeploy(string from, string to) - { - var finalTo = $"{StreamingAssetsDirectory}/{to}"; - m_RegisterAdditionalFilesToDeploy(from, finalTo); - } - } -} -#endif diff --git a/Unity.Scenes.Editor/EntitySectionBundlesBuildCustomizer.cs.meta b/Unity.Scenes.Editor/EntitySectionBundlesBuildCustomizer.cs.meta deleted file mode 100644 index 56362da..0000000 --- a/Unity.Scenes.Editor/EntitySectionBundlesBuildCustomizer.cs.meta +++ /dev/null @@ -1,3 +0,0 @@ -fileFormatVersion: 2 -guid: 347849b3165445f3999ab94b7f81c7f5 -timeCreated: 1603804543 \ No newline at end of file diff --git a/Unity.Scenes.Editor/LiveConversion/LiveConversionConnection.cs b/Unity.Scenes.Editor/LiveConversion/LiveConversionConnection.cs index 8525e70..61d1151 100644 --- a/Unity.Scenes.Editor/LiveConversion/LiveConversionConnection.cs +++ b/Unity.Scenes.Editor/LiveConversion/LiveConversionConnection.cs @@ -2,10 +2,6 @@ using System.Collections.Generic; using System.Reflection; using Unity.Collections; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -using Unity.Build.Common; -#endif using Unity.Entities; using Unity.Entities.Build; using Unity.Entities.Conversion; @@ -36,9 +32,6 @@ class LiveConversionConnection Dictionary _GUIDToEditScene = new Dictionary(); internal readonly Hash128 _ConfigurationGUID; -#if USING_PLATFORMS_PACKAGE - BuildConfiguration _BuildConfiguration; -#endif IEntitiesPlayerSettings _SettingAsset; UnityEngine.Hash128 _BuildConfigurationArtifactHash; @@ -219,17 +212,6 @@ void MarkAssetChanged(int assetInstanceId, Scene scene) } } - bool HasAssetDependencies() - { - foreach (var kvp in _SceneGUIDToLiveConversion) - { - if (kvp.Value.HasAssetDependencies()) - return true; - } - - return false; - } - public static void GlobalDirtyLiveConversion() { GlobalDirtyID++; @@ -414,11 +396,7 @@ void AddLiveConversionChangeSet(ref LiveConversionDiffGenerator liveConversion, try { var editScene = _GUIDToEditScene[sceneGUID]; - var didChange = LiveConversionDiffGenerator.UpdateLiveConversion(editScene, sceneGUID, ref liveConversion, mode, globalArtifactVersion, _ConfigurationGUID, -#if USING_PLATFORMS_PACKAGE - _BuildConfiguration, -#endif - _SettingAsset, out var changes); + var didChange = LiveConversionDiffGenerator.UpdateLiveConversion(editScene, sceneGUID, ref liveConversion, mode, globalArtifactVersion, _ConfigurationGUID, _SettingAsset, out var changes); if (didChange) changeSets.Add(changes); } diff --git a/Unity.Scenes.Editor/LiveConversion/LiveConversionDiffGenerator.cs b/Unity.Scenes.Editor/LiveConversion/LiveConversionDiffGenerator.cs index a5c26a7..3789e32 100644 --- a/Unity.Scenes.Editor/LiveConversion/LiveConversionDiffGenerator.cs +++ b/Unity.Scenes.Editor/LiveConversion/LiveConversionDiffGenerator.cs @@ -4,10 +4,6 @@ using System.Text; using Unity.Collections; using Unity.Entities; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -using Unity.Build.DotsRuntime; -#endif using Unity.Entities.Build; using Unity.Entities.Conversion; using Unity.Profiling; @@ -36,9 +32,6 @@ class LiveConversionDiffGenerator : IDisposable Scene _Scene; GUID _configGUID; - #if USING_PLATFORMS_PACKAGE - BuildConfiguration _buildConfiguration; - #endif IEntitiesPlayerSettings _settingAsset; readonly Hash128 _SceneGUID; readonly BlobAssetStore _BlobAssetStore; @@ -64,13 +57,6 @@ public void Dispose() internal IncrementalBakingChangeTracker ChangeTracker => _IncrementalBakingChangeTracker; - internal bool HasAssetDependencies() - { - var bakingSystem = _ConvertedWorld.GetExistingSystemManaged(); - var assetDependencies = bakingSystem.GetAllAssetDependencies(); - return assetDependencies.Length > 0; - } - public void RequestCleanConversion() { _RequestCleanConversion = true; @@ -86,18 +72,11 @@ public bool DidRequestUpdate(uint artifactDependencyVersion) return didChange; } - LiveConversionDiffGenerator(Scene scene, Hash128 sceneGUID, GUID configGUID, -#if USING_PLATFORMS_PACKAGE - BuildConfiguration buildConfig, -#endif - IEntitiesPlayerSettings settingAsset, bool liveConversionEnabled) + LiveConversionDiffGenerator(Scene scene, Hash128 sceneGUID, GUID configGUID, IEntitiesPlayerSettings settingAsset, bool liveConversionEnabled) { _SceneGUID = sceneGUID; _Scene = scene; _configGUID = configGUID; -#if USING_PLATFORMS_PACKAGE - _buildConfiguration = buildConfig; -#endif _settingAsset = settingAsset; _LiveConversionEnabled = liveConversionEnabled; @@ -206,11 +185,7 @@ bool Bake(BakingUtility.BakingFlags flags) _IncrementalConversionDebug.LastBakingFlags = flags; _IncrementalConversionDebug.NeedsUpdate = !_RequestCleanConversion; - var conversionSettings = GetBakeSettings(flags, _BlobAssetStore, -#if USING_PLATFORMS_PACKAGE - _buildConfiguration, -#endif - _settingAsset); + var conversionSettings = GetBakeSettings(flags, _BlobAssetStore, _settingAsset); var didBake = BakingUtility.BakeScene(_ConvertedWorld, _Scene, conversionSettings, !_RequestCleanConversion, _IncrementalBakingChangeTracker); if (didBake) AddMissingData(_ConvertedWorld, _MissingSceneQuery, _MissingRenderDataQuery, flags); @@ -226,18 +201,11 @@ bool Bake(BakingUtility.BakingFlags flags) } } - BakingSettings GetBakeSettings(BakingUtility.BakingFlags flags, BlobAssetStore store, -#if USING_PLATFORMS_PACKAGE - BuildConfiguration buildConfiguration, -#endif - IEntitiesPlayerSettings settingAssets) + BakingSettings GetBakeSettings(BakingUtility.BakingFlags flags, BlobAssetStore store, IEntitiesPlayerSettings settingAssets) { var conversionSettings = new BakingSettings(flags, store) { SceneGUID = _SceneGUID, -#if USING_PLATFORMS_PACKAGE - BuildConfiguration = buildConfiguration, -#endif DotsSettings = settingAssets, }; if (LiveConversionSettings.AdditionalConversionSystems.Count != 0) @@ -262,11 +230,7 @@ internal void DebugIncrementalConversion() var flags = _IncrementalConversionDebug.LastBakingFlags; // use this to compare the results of incremental conversion with the results of a clean conversion. - var conversionSettings = GetBakeSettings(flags, blobAssetStore, -#if USING_PLATFORMS_PACKAGE - _buildConfiguration, -#endif - _settingAsset); + var conversionSettings = GetBakeSettings(flags, blobAssetStore, _settingAsset); BakingUtility.BakeScene(_IncrementalConversionDebug.World, _Scene, conversionSettings, false, null); AddMissingData(_IncrementalConversionDebug.World, _IncrementalConversionDebug.MissingSceneQuery, @@ -384,18 +348,11 @@ static void AddMissingData(World world, EntityQuery missingSceneQuery, EntityQue } } - public static bool UpdateLiveConversion(Scene scene, Hash128 sceneGUID, ref LiveConversionDiffGenerator liveConversionData, LiveConversionMode mode, ulong globalAsssetDependencyVersion, GUID configGUID, -#if USING_PLATFORMS_PACKAGE - BuildConfiguration config, -#endif - IEntitiesPlayerSettings settingAsset, out LiveConversionChangeSet changes) + public static bool UpdateLiveConversion(Scene scene, Hash128 sceneGUID, ref LiveConversionDiffGenerator liveConversionData, LiveConversionMode mode, + ulong globalAsssetDependencyVersion, GUID configGUID, IEntitiesPlayerSettings settingAsset, out LiveConversionChangeSet changes) { -#if USING_PLATFORMS_PACKAGE - int framesToRetainBlobAssets = RetainBlobAssetsSetting.GetFramesToRetainBlobAssets(config); -#else // This should be removed or moved to a general setting int framesToRetainBlobAssets = 1; -#endif var liveConversionEnabled = mode != LiveConversionMode.Disabled; if (liveConversionData != null && liveConversionData._LiveConversionEnabled != liveConversionEnabled) @@ -419,22 +376,11 @@ public static bool UpdateLiveConversion(Scene scene, Hash128 sceneGUID, ref Live } if (liveConversionData == null) - liveConversionData = new LiveConversionDiffGenerator(scene, sceneGUID, configGUID, - #if USING_PLATFORMS_PACKAGE - config, - #endif - settingAsset, liveConversionEnabled); - else if (liveConversionData._Scene != scene -#if USING_PLATFORMS_PACKAGE - || !ReferenceEquals(liveConversionData._buildConfiguration, config) -#endif - || liveConversionData._configGUID != configGUID) + liveConversionData = new LiveConversionDiffGenerator(scene, sceneGUID, configGUID, settingAsset, liveConversionEnabled); + else if (liveConversionData._Scene != scene || liveConversionData._configGUID != configGUID) { liveConversionData._Scene = scene; liveConversionData._configGUID = configGUID; -#if USING_PLATFORMS_PACKAGE - liveConversionData._buildConfiguration = config; -#endif liveConversionData._RequestCleanConversion = true; } liveConversionData._ArtifactDependencyVersion = globalAsssetDependencyVersion; diff --git a/Unity.Scenes.Editor/ResourceCatalogBuildCode.cs b/Unity.Scenes.Editor/ResourceCatalogBuildCode.cs index 49ed90d..0103913 100644 --- a/Unity.Scenes.Editor/ResourceCatalogBuildCode.cs +++ b/Unity.Scenes.Editor/ResourceCatalogBuildCode.cs @@ -1,6 +1,3 @@ -#if USING_PLATFORMS_PACKAGE -using Unity.Build.Common; -#endif using Unity.Collections; using Unity.Entities; @@ -8,33 +5,6 @@ namespace Unity.Scenes.Editor { static class ResourceCatalogBuildCode { -#if USING_PLATFORMS_PACKAGE - public static void WriteCatalogFile(SceneList sceneList, string sceneInfoPath) - { - var dir = System.IO.Path.GetDirectoryName(sceneInfoPath); - System.IO.Directory.CreateDirectory(dir); - var sceneInfos = sceneList.GetSceneInfosForBuild(); - var builder = new BlobBuilder(Allocator.Temp); - ref var root = ref builder.ConstructRoot(); - var metas = builder.Allocate(ref root.resources, sceneInfos.Length); - for (int i = 0; i < sceneInfos.Length; i++) - { - metas[i] = new ResourceMetaData() - { - ResourceId = sceneInfos[i].Scene.assetGUID, - ResourceType = ResourceMetaData.Type.Scene, - }; - } - - var strings = builder.Allocate(ref root.paths, sceneInfos.Length); - for (int i = 0; i < sceneInfos.Length; i++) - builder.AllocateString(ref strings[i], sceneInfos[i].Path); - - BlobAssetReference.Write(builder, sceneInfoPath, ResourceCatalogData.CurrentFileFormatVersion); - builder.Dispose(); - } -#endif - public static void WriteCatalogFile(RootSceneInfo[] sceneArray, string sceneInfoPath) { var dir = System.IO.Path.GetDirectoryName(sceneInfoPath); diff --git a/Unity.Scenes.Editor/RetainBlobAssetsSetting.cs b/Unity.Scenes.Editor/RetainBlobAssetsSetting.cs deleted file mode 100644 index 91ffafb..0000000 --- a/Unity.Scenes.Editor/RetainBlobAssetsSetting.cs +++ /dev/null @@ -1,18 +0,0 @@ -#if USING_PLATFORMS_PACKAGE -using Unity.Build; - -class RetainBlobAssetsSetting : IBuildComponent -{ - public int FramesToRetainBlobAssets; - - public string Name => "RetainBlobAssetsSetting"; - - public static int GetFramesToRetainBlobAssets(BuildConfiguration config) - { - int framesToRetainBlobAssets = 1; - if (config != null && config.TryGetComponent(out RetainBlobAssetsSetting retainSetting)) - framesToRetainBlobAssets = retainSetting.FramesToRetainBlobAssets; - return framesToRetainBlobAssets; - } -} -#endif diff --git a/Unity.Scenes.Editor/RetainBlobAssetsSetting.cs.meta b/Unity.Scenes.Editor/RetainBlobAssetsSetting.cs.meta deleted file mode 100644 index 8195d6c..0000000 --- a/Unity.Scenes.Editor/RetainBlobAssetsSetting.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: bc7057caf27ea4daeade689fcbdcca51 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Scenes.Editor/SubSceneContextMenu.cs b/Unity.Scenes.Editor/SubSceneContextMenu.cs index 8f3a600..fa4b5f2 100644 --- a/Unity.Scenes.Editor/SubSceneContextMenu.cs +++ b/Unity.Scenes.Editor/SubSceneContextMenu.cs @@ -9,10 +9,13 @@ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Unity.Scenes.Hybrid.Tests.Editmode.Content")] +[assembly: InternalsVisibleTo("Unity.Environment.Editor")] namespace Unity.Scenes.Editor { internal class SubSceneContextMenu { + private const string k_DefaultFilename = "New Sub Scene"; + internal enum NewSubSceneMode { EmptyScene, @@ -21,18 +24,20 @@ internal enum NewSubSceneMode internal struct NewSubSceneArgs { - public NewSubSceneArgs(GameObject target, Scene parentScene, NewSubSceneMode mode) + public NewSubSceneArgs(GameObject target, Scene parentScene, NewSubSceneMode mode, string defaultFilename = null) { if (target == null && !parentScene.isLoaded) throw new ArgumentException("Missing info for new Sub Scene: Neither GameObject target nor parent scene is valid"); this.target = target; this.parentScene = target != null ? target.scene : parentScene; newSubSceneMode = mode; + this.defaultFilename = defaultFilename; } public GameObject target; public Scene parentScene; public NewSubSceneMode newSubSceneMode; + public string defaultFilename; } internal static SubScene CreateNewSubSceneAtPath(string path, NewSubSceneArgs args, InteractionMode interactionMode) @@ -79,7 +84,7 @@ static void CheckInputArguments(NewSubSceneArgs args, out Scene parentScene, out internal static SubScene CreateSubSceneAndAddSelection(GameObject gameObject, InteractionMode interactionMode = InteractionMode.AutomatedAction) { - var args = new NewSubSceneArgs(gameObject, default(Scene), NewSubSceneMode.MoveSelectionToScene); + var args = new NewSubSceneArgs(gameObject, default(Scene), NewSubSceneMode.MoveSelectionToScene, "New Sub Scene"); return CreateNewSubScene(gameObject.name, args, interactionMode); } @@ -94,13 +99,13 @@ static void AddNewSubSceneMenuItems(GenericMenu menu, GameObject target) var parentScene = target != null ? target.scene : GetLastRootScene(); var validForEmptyScene = validTarget || !string.IsNullOrEmpty(parentScene.path); if (!EditorApplication.isPlaying && validForEmptyScene) - menu.AddItem(newEmptySubScene, false, OnMenuItemForNewSubScene, new NewSubSceneArgs(target, parentScene, NewSubSceneMode.EmptyScene)); + menu.AddItem(newEmptySubScene, false, OnMenuItemForNewSubScene, new NewSubSceneArgs(target, parentScene, NewSubSceneMode.EmptyScene, k_DefaultFilename)); else menu.AddDisabledItem(newEmptySubScene); var addSubSceneContent = EditorGUIUtility.TrTextContent("New Sub Scene/From Selection..."); if (!EditorApplication.isPlaying && validTarget) - menu.AddItem(addSubSceneContent, false, OnMenuItemForNewSubScene, new NewSubSceneArgs(target, default(Scene), NewSubSceneMode.MoveSelectionToScene)); + menu.AddItem(addSubSceneContent, false, OnMenuItemForNewSubScene, new NewSubSceneArgs(target, default(Scene), NewSubSceneMode.MoveSelectionToScene, k_DefaultFilename)); else menu.AddDisabledItem(addSubSceneContent); } @@ -121,7 +126,7 @@ internal static void AddExtraSceneHeaderContextMenuItems(GenericMenu menu, Scene var newEmptySubScene = EditorGUIUtility.TrTextContent("New Empty Sub Scene..."); var validTarget = target.isLoaded; if (!EditorApplication.isPlaying && validTarget) - menu.AddItem(newEmptySubScene, false, OnMenuItemForNewSubScene, new NewSubSceneArgs(null, target, NewSubSceneMode.EmptyScene)); + menu.AddItem(newEmptySubScene, false, OnMenuItemForNewSubScene, new NewSubSceneArgs(null, target, NewSubSceneMode.EmptyScene, k_DefaultFilename)); else menu.AddDisabledItem(newEmptySubScene); } @@ -166,25 +171,33 @@ static void DeleteCreatedStartFolderIfNotUsed(string startFolder, string savePat static void OnMenuItemForNewSubScene(object userData) { var args = (NewSubSceneArgs)userData; + PromptUserForNewSubscene(args); + } + /// + /// Display a file picker dialog for the user to create a new Sub Scene + /// + /// The creation settings to use + /// The created Sub Scene or null if the operation failed or was canceled + internal static SubScene PromptUserForNewSubscene(NewSubSceneArgs args) + { if (args.newSubSceneMode == NewSubSceneMode.MoveSelectionToScene) { var topLevelSelection = GetValidSelectedGameObjectsForSubSceneCreation(args.target); if (topLevelSelection == null) - return; + return null; try { ThrowIfAnyGameObjectsCannotBeMovedToSubScene(topLevelSelection); } catch (ArgumentException e) { EditorUtility.DisplayDialog("Cannot Move Selection To Sub Scene", e.Message, "OK"); - return; + return null; } } var parentScene = args.parentScene.isLoaded ? args.parentScene : args.target.scene; var parentSceneName = Path.GetFileNameWithoutExtension(parentScene.path); var startFolder = Path.Combine(Path.GetDirectoryName(parentScene.path), parentSceneName); - var baseName = "New Sub Scene"; - var startPath = GetActualPathName(Path.Combine(startFolder, baseName + ".unity")); + var startPath = GetActualPathName(Path.Combine(startFolder, args.defaultFilename ?? k_DefaultFilename + ".unity")); if (Directory.Exists(startFolder)) startPath = AssetDatabase.GenerateUniqueAssetPath(startPath); @@ -223,8 +236,9 @@ static void OnMenuItemForNewSubScene(object userData) if (File.Exists(savePath)) AssetDatabase.DeleteAsset(savePath); - CreateNewSubSceneAtPath(savePath, args, InteractionMode.UserAction); + return CreateNewSubSceneAtPath(savePath, args, InteractionMode.UserAction); } + return null; } static string GetSubSceneFilePathUnderParentSceneFilePath(Scene parentScene, string newSubSceneName) diff --git a/Unity.Scenes.Editor/SubSceneFilesProvider.cs b/Unity.Scenes.Editor/SubSceneFilesProvider.cs deleted file mode 100644 index 9e76236..0000000 --- a/Unity.Scenes.Editor/SubSceneFilesProvider.cs +++ /dev/null @@ -1,52 +0,0 @@ -#if USING_PLATFORMS_PACKAGE -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using NUnit.Framework; -using Unity.Build.Classic; -using Unity.Build.Common; -using Unity.Entities; -using UnityEditor; -using UnityEditor.Experimental; - -namespace Unity.Scenes.Editor -{ - class SubSceneFilesProvider : ClassicBuildPipelineCustomizer - { - public override Type[] UsedComponents { get; } = - { - typeof(SceneList), - typeof(ClassicBuildProfile) - }; - - public override void OnBeforeRegisterAdditionalFilesToDeploy() - { - if (BuildTarget == BuildTarget.NoTarget) - throw new InvalidOperationException($"Invalid build target '{BuildTarget.ToString()}'."); - - if (BuildTarget != EditorUserBuildSettings.activeBuildTarget) - throw new InvalidOperationException($"ActiveBuildTarget must be switched before the {nameof(EntitySceneBuildUtility)} runs."); - - var sceneList = Context.GetComponentOrDefault(); - var binaryFiles = Context.GetOrCreateValue(); - var buildTargetGUID = new Hash128(Context.BuildConfigurationAssetGUID); - var scenePaths = sceneList.GetScenePathsForBuild(); - var subSceneGuids = new HashSet(scenePaths.SelectMany(scenePath => EditorEntityScenes.GetSubScenes(AssetDatabaseCompatibility.PathToGUID(scenePath)))); - var artifactKeys = new Dictionary(); - - EntitySceneBuildUtility.PrepareEntityBinaryArtifacts(buildTargetGUID, subSceneGuids, artifactKeys); - - // Put in component to pass data further along in build - binaryFiles.Add(artifactKeys.Keys, artifactKeys.Values); - } - - public override void RegisterAdditionalFilesToDeploy(Action registerAdditionalFileToDeploy) - { - var tempFile = Path.GetFullPath(Path.Combine(WorkingDirectory, EntityScenesPaths.RelativePathForSceneInfoFile)); - ResourceCatalogBuildCode.WriteCatalogFile(Context.GetComponentOrDefault(), tempFile); - registerAdditionalFileToDeploy(tempFile, EntityScenesPaths.FullPathForFile(StreamingAssetsDirectory, EntityScenesPaths.RelativePathForSceneInfoFile)); - } - } -} -#endif diff --git a/Unity.Scenes.Editor/SubSceneFilesProvider.cs.meta b/Unity.Scenes.Editor/SubSceneFilesProvider.cs.meta deleted file mode 100644 index 13a5d28..0000000 --- a/Unity.Scenes.Editor/SubSceneFilesProvider.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 3f44ba1337df2034a9bb85adc5ee7817 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Scenes.Editor/SubSceneImporter.cs b/Unity.Scenes.Editor/SubSceneImporter.cs index 02eb985..8320f37 100644 --- a/Unity.Scenes.Editor/SubSceneImporter.cs +++ b/Unity.Scenes.Editor/SubSceneImporter.cs @@ -3,10 +3,6 @@ using Unity.Collections; using Unity.Entities; using Unity.Entities.Serialization; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -using Unity.Build.DotsRuntime; -#endif using Unity.Core.Compression; using Unity.Entities.Build; using UnityEditor; @@ -99,11 +95,7 @@ static unsafe void WriteGlobalUsageArtifact(BuildUsageTagGlobal globalUsage, Ass } } - void ImportBaking(AssetImportContext ctx, Scene scene, SceneWithBuildConfigurationGUIDs sceneWithBuildConfiguration, IEntitiesPlayerSettings settingsAsset, -#if USING_PLATFORMS_PACKAGE - BuildConfiguration buildConfig, -#endif - GameObject prefab) + void ImportBaking(AssetImportContext ctx, Scene scene, SceneWithBuildConfigurationGUIDs sceneWithBuildConfiguration, IEntitiesPlayerSettings settingsAsset, GameObject prefab) { // Grab the used lighting and fog values from the scenes lighting & render settings. // If autobake is enabled, this function will iterate through objects to predict the outcome. @@ -116,9 +108,6 @@ void ImportBaking(AssetImportContext ctx, Scene scene, SceneWithBuildConfigurati { SceneGUID = sceneWithBuildConfiguration.SceneGUID, DotsSettings = settingsAsset, -#if USING_PLATFORMS_PACKAGE - BuildConfiguration = buildConfig, -#endif }; if (!sceneWithBuildConfiguration.IsBuildingForEditor) @@ -129,29 +118,6 @@ void ImportBaking(AssetImportContext ctx, Scene scene, SceneWithBuildConfigurati settings.FilterFlags = WorldSystemFilterFlags.BakingSystem; WriteEntitySceneSettings writeEntitySettings = new WriteEntitySceneSettings(); - //Dots runtime builds will still use build config -#if USING_PLATFORMS_PACKAGE - if (buildConfig != null) - { - if (buildConfig.TryGetComponent(out var rootAssembly)) - { - writeEntitySettings.Codec = Codec.LZ4; - writeEntitySettings.IsDotsRuntime = true; - writeEntitySettings.BuildAssemblyCache = new BuildAssemblyCache() - { - BaseAssemblies = rootAssembly.RootAssembly.asset, - PlatformName = EditorUserBuildSettings.activeBuildTarget.ToString() - }; - - //Updating the root asmdef references or its references should re-trigger conversion - ctx.DependsOnArtifact(AssetDatabase.GetAssetPath(rootAssembly.RootAssembly.asset)); - foreach (var assemblyPath in writeEntitySettings.BuildAssemblyCache.AssembliesPath) - { - ctx.DependsOnArtifact(assemblyPath); - } - } - } -#endif var sectionRefObjs = new List(); var sectionData = EditorEntityScenes.BakeAndWriteEntityScene(scene, settings, sectionRefObjs, writeEntitySettings); @@ -163,21 +129,9 @@ void ImportBaking(AssetImportContext ctx, Scene scene, SceneWithBuildConfigurati DestroyImmediate(objRefs); } - void GetBuildConfigurationOrDotsSettings(SceneWithBuildConfigurationGUIDs sceneWithBuildConfiguration, - out IEntitiesPlayerSettings settingsAsset -#if USING_PLATFORMS_PACKAGE - , out BuildConfiguration buildConfig -#endif - ) + void GetBuildConfigurationOrDotsSettings(SceneWithBuildConfigurationGUIDs sceneWithBuildConfiguration, out IEntitiesPlayerSettings settingsAsset) { settingsAsset = null; -#if USING_PLATFORMS_PACKAGE - buildConfig = null; - buildConfig = BuildConfiguration.LoadAsset(sceneWithBuildConfiguration.BuildConfiguration); - if (buildConfig != null) - return; - // If we failed to load a BuildConfiguration asset, let's try to load a IEntitiesPlayerSettings one -#endif // ensure the settings objects are updated and contain the latest changes from the editor DotsGlobalSettings.Instance.ReloadSettingsObjects(); @@ -221,11 +175,7 @@ public override void OnImportAsset(AssetImportContext ctx) EditorEntityScenes.AddEntityBinaryFileDependencies(ctx, sceneWithBuildConfiguration.BuildConfiguration); EditorEntityScenes.DependOnSceneGameObjects(sceneWithBuildConfiguration.SceneGUID, ctx); - GetBuildConfigurationOrDotsSettings(sceneWithBuildConfiguration, out var settingsAsset -#if USING_PLATFORMS_PACKAGE - , out var buildConfig -#endif - ); + GetBuildConfigurationOrDotsSettings(sceneWithBuildConfiguration, out var settingsAsset); if(settingsAsset != null) ctx.DependsOnCustomDependency(settingsAsset.CustomDependency); @@ -249,12 +199,7 @@ public override void OnImportAsset(AssetImportContext ctx) try { EditorSceneManager.SetActiveScene(scene); - - ImportBaking(ctx, scene, sceneWithBuildConfiguration, settingsAsset, -#if USING_PLATFORMS_PACKAGE - buildConfig, -#endif - prefab); + ImportBaking(ctx, scene, sceneWithBuildConfiguration, settingsAsset, prefab); } finally { diff --git a/Unity.Scenes.Editor/SubSceneInspectorUtility.cs b/Unity.Scenes.Editor/SubSceneInspectorUtility.cs index 5facac5..88776fc 100644 --- a/Unity.Scenes.Editor/SubSceneInspectorUtility.cs +++ b/Unity.Scenes.Editor/SubSceneInspectorUtility.cs @@ -1,8 +1,5 @@ using System; using System.Collections.Generic; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -#endif using Unity.Collections; using Unity.Entities; using Unity.Mathematics; @@ -15,7 +12,7 @@ namespace Unity.Scenes.Editor { [InitializeOnLoad] - internal static class SubSceneInspectorUtility + public static class SubSceneInspectorUtility { internal delegate void RepaintAction(); diff --git a/Unity.Scenes.Editor/TimeBasedCallbackInvoker.cs b/Unity.Scenes.Editor/TimeBasedCallbackInvoker.cs deleted file mode 100644 index 4d97bde..0000000 --- a/Unity.Scenes.Editor/TimeBasedCallbackInvoker.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Generic; -using UnityEditor; - -namespace Unity.Scenes.Editor -{ - /// - /// Invokes registered callbacks during static editor update with a fixed timestep. - /// - static class TimeBasedCallbackInvoker - { - static List m_RegisterdCallbacks = new List(); - const double k_TimeInterval = 0.05f; - static double m_NextInvocationTime; - - /// - /// Register a callback for repeated invocation. - /// The registration will have no effect if the callback has already been registered. - /// - public static void SetCallback(EditorApplication.CallbackFunction callback) - { - if (m_RegisterdCallbacks.Contains(callback)) - return; - - m_RegisterdCallbacks.Add(callback); - - if (m_RegisterdCallbacks.Count == 1) - { - EditorApplication.update += InvokeCallbacks; - m_NextInvocationTime = EditorApplication.timeSinceStartup + k_TimeInterval; - } - } - - /// - /// Stop repeated invocation of a callback. - /// - public static void ClearCallback(EditorApplication.CallbackFunction callback) - { - if (!m_RegisterdCallbacks.Contains(callback)) - return; - - m_RegisterdCallbacks.Remove(callback); - - if (m_RegisterdCallbacks.Count == 0) - EditorApplication.update -= InvokeCallbacks; - } - - static void InvokeCallbacks() - { - var currentTime = EditorApplication.timeSinceStartup; - if (currentTime < m_NextInvocationTime) - return; - - m_NextInvocationTime = currentTime + k_TimeInterval; - for (var i = m_RegisterdCallbacks.Count - 1; i >= 0; i--) - m_RegisterdCallbacks[i].Invoke(); - } - } -} diff --git a/Unity.Scenes.Editor/TimeBasedCallbackInvoker.cs.meta b/Unity.Scenes.Editor/TimeBasedCallbackInvoker.cs.meta deleted file mode 100644 index a1f8be9..0000000 --- a/Unity.Scenes.Editor/TimeBasedCallbackInvoker.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 9d1f1e53e9e0349aba35f8136491c031 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Scenes.Editor/TypeDependencyCache.cs b/Unity.Scenes.Editor/TypeDependencyCache.cs index f1696f6..62cb611 100644 --- a/Unity.Scenes.Editor/TypeDependencyCache.cs +++ b/Unity.Scenes.Editor/TypeDependencyCache.cs @@ -6,9 +6,6 @@ using UnityEngine; using UnityEditor.AssetImporters; using System.Reflection; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -#endif using Unity.Entities.Conversion; using Unity.Entities.Serialization; using Unity.Profiling; @@ -90,38 +87,80 @@ static void RegisterComponentTypes() } } + static int CompareType(BakingTypeAndFullName a, BakingTypeAndFullName b) + { + return string.CompareOrdinal(a.FullName, b.FullName); + } + + internal struct BakingTypeAndFullName + { + public Type Type; + public string FullName; + } + static unsafe void RegisterBakingAssemblies() { - var bakers = TypeCache.GetTypesDerivedFrom(); - var systems = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.BakingSystem | WorldSystemFilterFlags.EntitySceneOptimizations); - BakingNameAndVersion[] versionedAssemblies = new BakingNameAndVersion[bakers.Count + systems.Count]; + var bakersTypeCollection = TypeCache.GetTypesDerivedFrom(); + var systemsReadOnlyList = DefaultWorldInitialization.GetAllSystems(WorldSystemFilterFlags.BakingSystem | WorldSystemFilterFlags.EntitySceneOptimizations); + int bakersCount = bakersTypeCollection.Count; + int systemsCount = systemsReadOnlyList.Count; + BakingNameAndVersion[] versionedAssemblies = new BakingNameAndVersion[bakersCount + systemsCount]; + + BakingTypeAndFullName[] bakers = new BakingTypeAndFullName[bakersCount]; + var emptyString = string.Empty; + for (int i = 0; i < bakersCount; i++) + { + var currentType = bakersTypeCollection[i]; + var fullName = currentType.FullName; + fullName = fullName == null ? emptyString : fullName; + ref BakingTypeAndFullName bakingNameAndVersion = ref bakers[i]; + bakingNameAndVersion.FullName = fullName; + bakingNameAndVersion.Type = currentType; + } + + Array.Sort(bakers, CompareType); + + List systemsAsList = systemsReadOnlyList as List; + + BakingTypeAndFullName[] systems = new BakingTypeAndFullName[systemsCount]; + for (int i = 0; i < systemsCount; i++) + { + var currentType = systemsAsList[i]; + var fullName = currentType.FullName; + fullName = fullName == null ? emptyString : fullName; + ref BakingTypeAndFullName bakingNameAndVersion = ref systems[i]; + bakingNameAndVersion.FullName = fullName; + bakingNameAndVersion.Type = currentType; + } + + Array.Sort(systems, CompareType); int count = 0; // baker versions - for (int i = 0; i != bakers.Count; i++) + for (int i = 0; i != bakersCount; i++) { var fullName = bakers[i].FullName; if (fullName == null) continue; - var bakingVersionAttribute = bakers[i].GetCustomAttribute(); + var bakingVersionAttribute = bakers[i].Type.GetCustomAttribute(); if ( bakingVersionAttribute != null) { - versionedAssemblies[count++].Init(bakingVersionAttribute, bakers[i], fullName); + versionedAssemblies[count++].Init(bakingVersionAttribute, bakers[i].Type, fullName); } } // baking system versions - for (int i = 0; i != systems.Count; i++) + for (int i = 0; i != systemsCount; i++) { var fullName = systems[i].FullName; if (fullName == null) continue; - var bakingVersionAttribute = systems[i].GetCustomAttribute(); + var bakingVersionAttribute = systems[i].Type.GetCustomAttribute(); if ( bakingVersionAttribute != null) { - versionedAssemblies[count++].Init(bakingVersionAttribute, systems[i], fullName); + versionedAssemblies[count++].Init(bakingVersionAttribute, systems[i].Type, fullName); } } @@ -129,8 +168,15 @@ static unsafe void RegisterBakingAssemblies() UnityEngine.Hash128 hash = default; Dictionary> missingBakingVersionAttributePerAssembly = new Dictionary>(); - foreach (var bakerType in bakers) + for (int i = 0; i != bakersCount; i++) { + Type bakerType = bakers[i].Type; + + // If this baker has the BakingVersion attribute set to Excluded we don't need to validate it (it can be in an assembly with or without BakingVersions) + var isExcluded = Array.Exists(versionedAssemblies, x => x.Excluded && x.Type == bakerType); + if (isExcluded) + continue; + var assembly = bakerType.Assembly; var assemblyName = assembly.GetName().Name; //If there is at least one baker marked with BakingVersion attribute, we don't register the dependency with the assembly but the value of the attribute @@ -178,8 +224,15 @@ static unsafe void RegisterBakingAssemblies() } } - foreach (var systemType in systems) + for (int i = 0; i != systemsCount; i++) { + Type systemType = systems[i].Type; + + // If this baking system has the BakingVersion attribute set to Excluded we don't need to validate it (it can be in an assembly with or without BakingVersions) + var isExcluded = Array.Exists(versionedAssemblies, x => x.Excluded && x.Type == systemType); + if (isExcluded) + continue; + var assembly = systemType.Assembly; var assemblyName = assembly.GetName().Name; //If there is at least one baking system or entity scene optimization system marked with BakingVersion attribute, we don't register the dependency with the assembly but the value of the attribute diff --git a/Unity.Scenes.Editor/Unity.Scenes.Editor.asmdef b/Unity.Scenes.Editor/Unity.Scenes.Editor.asmdef index 6385250..013e4be 100644 --- a/Unity.Scenes.Editor/Unity.Scenes.Editor.asmdef +++ b/Unity.Scenes.Editor/Unity.Scenes.Editor.asmdef @@ -25,12 +25,6 @@ "precompiledReferences": [], "autoReferenced": true, "defineConstraints": [], - "versionDefines": [ - { - "name": "com.unity.platforms", - "expression": "", - "define": "USING_PLATFORMS_PACKAGE" - } - ], + "versionDefines": [], "noEngineReferences": false } diff --git a/Unity.Scenes.Hybrid.Tests/Editmode/Content/RuntimeContentManagerTests.cs b/Unity.Scenes.Hybrid.Tests/Editmode/Content/RuntimeContentManagerTests.cs index 0ef0725..4a92f0e 100644 --- a/Unity.Scenes.Hybrid.Tests/Editmode/Content/RuntimeContentManagerTests.cs +++ b/Unity.Scenes.Hybrid.Tests/Editmode/Content/RuntimeContentManagerTests.cs @@ -1,4 +1,4 @@ -#if !UNITY_DISABLE_MANAGED_COMPONENTS && !UNITY_DOTSRUNTIME +#if !UNITY_DISABLE_MANAGED_COMPONENTS using NUnit.Framework; using Unity.Entities.Content; using Unity.Entities.Serialization; @@ -20,9 +20,6 @@ using UnityEditor.SceneManagement; using Unity.Scenes.Editor; using Unity.Scenes.Editor.Tests; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -#endif #endif @@ -201,10 +198,6 @@ void ReleaseThreadFunc(object context) p.Item2.Set(); } -#if false - // APV doesn't respect the Ignore attribute to disable tests, so ifdef explicitly - // https://unity.slack.com/archives/C04UGPY27S9/p1683136704435259 - [Ignore("Needs ADDR-3368 to work reliably on APV.")] [UnityTest] public IEnumerator RuntimeContentManager_CanLoadAndReleaseFromThreads() { @@ -245,7 +238,6 @@ public IEnumerator RuntimeContentManager_CanLoadAndReleaseFromThreads() } ids.Dispose(); } -#endif struct LoadObjectJob : IJob { @@ -264,11 +256,6 @@ public void Execute() RuntimeContentManager.ReleaseObjectAsync(id); } } - -#if false - // APV doesn't respect the Ignore attribute to disable tests, so ifdef explicitly - // https://unity.slack.com/archives/C04UGPY27S9/p1683136704435259 - [Ignore("Needs ADDR-3368 to work reliably on APV.")] [UnityTest] public IEnumerator RuntimeContentManager_CanLoadAdditive_GOScenes() { @@ -337,7 +324,6 @@ public IEnumerator RuntimeContentManager_CanLoadAndReleaseFromJobs() } ids.Dispose(); } -#endif IEnumerator AssertCanLoadAndRelease(UntypedWeakReferenceId id) where TObject : UnityEngine.Object { @@ -353,10 +339,7 @@ IEnumerator AssertCanLoadAndRelease(UntypedWeakReferenceId id) where TO RuntimeContentManager.ProcessQueuedCommands(); Assert.AreEqual(ObjectLoadingStatus.None, RuntimeContentManager.GetObjectLoadingStatus(id)); } - -#if false - // APV doesn't respect the Ignore attribute to disable tests, so ifdef explicitly - // https://unity.slack.com/archives/C04UGPY27S9/p1683136704435259 + [Test] public void LoadingObjectsCountIsCorrectAfterLoadsAndReleases() { @@ -376,7 +359,6 @@ public void LoadingObjectsCountIsCorrectAfterLoadsAndReleases() ids.Dispose(); } - [Ignore("Needs ADDR-3368 to work reliably on APV.")] [UnityTest] public IEnumerator RuntimeContentManager_CanLoadLocalAssets() { @@ -416,7 +398,7 @@ public IEnumerator RuntimeContentManager_CanLoadLocalInstances() RuntimeContentManager.ReleaseInstancesAsync(handle2); RuntimeContentManager.ProcessQueuedCommands(); } -#endif + bool InitializeCatalogForTest() { @@ -426,9 +408,6 @@ bool InitializeCatalogForTest() return RuntimeContentManager.LoadLocalCatalogData(catalogPath, RuntimeContentManager.DefaultContentFileNameFunc, f => $"{TestStreamingAssetsFullPath}/{RuntimeContentManager.DefaultArchivePathFunc(f)}"); } -#if false - // APV doesn't respect the Ignore attribute to disable tests, so ifdef explicitly - // https://unity.slack.com/archives/C04UGPY27S9/p1683136704435259 [UnityTest] #if UNITY_EDITOR_LINUX [Ignore("DOTS-7790 - Ubuntu editor often crashes when running this test")] @@ -457,6 +436,7 @@ public IEnumerator WeakObjectReference_CanLoadAndRelease() Assert.AreEqual(ObjectLoadingStatus.None, matRef.LoadingStatus); } + [Test] public void WeakObjectReference_WhenNotLoaded_LoadingStatus_IsNone() { @@ -516,7 +496,6 @@ public void WeakObjectReference_IsReferenceValid_ReturnsFalse_When_Asset_DoesntE Assert.IsFalse(wr.IsReferenceValid); } -#endif } } #endif diff --git a/Unity.Scenes.Hybrid.Tests/Editmode/Content/Unity.Scenes.Hybrid.Tests.Editmode.Content.asmdef b/Unity.Scenes.Hybrid.Tests/Editmode/Content/Unity.Scenes.Hybrid.Tests.Editmode.Content.asmdef index c9ad1b2..3a99e7d 100644 --- a/Unity.Scenes.Hybrid.Tests/Editmode/Content/Unity.Scenes.Hybrid.Tests.Editmode.Content.asmdef +++ b/Unity.Scenes.Hybrid.Tests/Editmode/Content/Unity.Scenes.Hybrid.Tests.Editmode.Content.asmdef @@ -32,12 +32,6 @@ "defineConstraints": [ "UNITY_INCLUDE_TESTS" ], - "versionDefines": [ - { - "name": "com.unity.platforms", - "expression": "", - "define": "USING_PLATFORMS_PACKAGE" - } - ], + "versionDefines": [], "noEngineReferences": false } diff --git a/Unity.Scenes.Hybrid.Tests/Editmode/Unity.Scenes.Hybrid.Tests.asmdef b/Unity.Scenes.Hybrid.Tests/Editmode/Unity.Scenes.Hybrid.Tests.asmdef index a3a936d..e8403ce 100644 --- a/Unity.Scenes.Hybrid.Tests/Editmode/Unity.Scenes.Hybrid.Tests.asmdef +++ b/Unity.Scenes.Hybrid.Tests/Editmode/Unity.Scenes.Hybrid.Tests.asmdef @@ -34,12 +34,6 @@ "defineConstraints": [ "UNITY_INCLUDE_TESTS" ], - "versionDefines": [ - { - "name": "com.unity.platforms", - "expression": "", - "define": "USING_PLATFORMS_PACKAGE" - } - ], + "versionDefines": [], "noEngineReferences": false } diff --git a/Unity.Scenes.Hybrid.Tests/Playmode/SubSceneTestFixture.cs b/Unity.Scenes.Hybrid.Tests/Playmode/SubSceneTestFixture.cs index 8016f95..3c20eb2 100644 --- a/Unity.Scenes.Hybrid.Tests/Playmode/SubSceneTestFixture.cs +++ b/Unity.Scenes.Hybrid.Tests/Playmode/SubSceneTestFixture.cs @@ -7,10 +7,6 @@ using UnityEditor; using Unity.Entities.Build; using UnityEditor.SceneManagement; -#if USING_PLATFORMS_PACKAGE -using Unity.Build; -using Unity.Build.Common; -#endif #endif using Hash128 = Unity.Entities.Hash128; diff --git a/Unity.Scenes.Hybrid.Tests/Playmode/SubSceneTests.cs b/Unity.Scenes.Hybrid.Tests/Playmode/SubSceneTests.cs index 3b822af..e268cc8 100644 --- a/Unity.Scenes.Hybrid.Tests/Playmode/SubSceneTests.cs +++ b/Unity.Scenes.Hybrid.Tests/Playmode/SubSceneTests.cs @@ -44,12 +44,12 @@ public void OneTimeTeardown() } [UnityTest] - [UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)] public IEnumerator LoadMultipleSubscenes_Async_WithAssetBundles() { using (var worldA = CreateEntityWorld("World A")) using (var worldB = CreateEntityWorld("World B")) { + #if UNITY_EDITOR Assert.IsTrue(PlayModeSceneGUID.IsValid); @@ -57,7 +57,7 @@ public IEnumerator LoadMultipleSubscenes_Async_WithAssetBundles() var worldBScene = SceneSystem.LoadSceneAsync(worldB.Unmanaged, PlayModeSceneGUID); #else Assert.IsTrue(BuildSceneGUID.IsValid); - + var initialContentFilesNumber = Loading.ContentLoadInterface.GetContentFiles(Unity.Entities.Content.RuntimeContentManager.Namespace).Length; var worldAScene = SceneSystem.LoadSceneAsync(worldA.Unmanaged, BuildSceneGUID); var worldBScene = SceneSystem.LoadSceneAsync(worldB.Unmanaged, BuildSceneGUID); #endif @@ -102,7 +102,7 @@ public IEnumerator LoadMultipleSubscenes_Async_WithAssetBundles() Assert.AreSame(sharedA.material, sharedB.material); Assert.IsTrue(sharedA.material != null, "sharedA.material != null"); #if !UNITY_EDITOR - Assert.AreEqual(2, Loading.ContentLoadInterface.GetContentFiles(Unity.Entities.Content.RuntimeContentManager.Namespace).Length); + var contentFilesNumberAfterLoadingScene = Loading.ContentLoadInterface.GetContentFiles(Unity.Entities.Content.RuntimeContentManager.Namespace).Length; #endif SceneSystem.UnloadScene(worldA.Unmanaged, worldAScene); @@ -111,14 +111,13 @@ public IEnumerator LoadMultipleSubscenes_Async_WithAssetBundles() worldA.Update(); worldB.Update(); #if !UNITY_EDITOR - Assert.AreEqual(0, Loading.ContentLoadInterface.GetContentFiles(Unity.Entities.Content.RuntimeContentManager.Namespace).Length); + var contentFilesNumberAfterUnLoadingScene = contentFilesNumberAfterLoadingScene - initialContentFilesNumber; + Assert.AreEqual(2, contentFilesNumberAfterUnLoadingScene); #endif } } - // Only works in Editor for now until we can support SubScene building with new build settings in a test [UnityTest] - [UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)] public IEnumerator LoadMultipleSubscenes_Blocking_WithAssetBundles() { using (var worldA = CreateEntityWorld("World A")) @@ -139,7 +138,7 @@ public IEnumerator LoadMultipleSubscenes_Blocking_WithAssetBundles() var worldBScene = SceneSystem.LoadSceneAsync(worldB.Unmanaged, PlayModeSceneGUID, loadParams); #else Assert.IsTrue(BuildSceneGUID.IsValid); - + var initialContentFilesNumber = Loading.ContentLoadInterface.GetContentFiles(Unity.Entities.Content.RuntimeContentManager.Namespace).Length; var worldAScene = SceneSystem.LoadSceneAsync(worldA.Unmanaged, BuildSceneGUID, loadParams); var worldBScene = SceneSystem.LoadSceneAsync(worldB.Unmanaged, BuildSceneGUID, loadParams); #endif @@ -193,7 +192,7 @@ public IEnumerator LoadMultipleSubscenes_Blocking_WithAssetBundles() Assert.IsTrue(sharedA.material != null, "sharedA.material != null"); #if !UNITY_EDITOR - Assert.AreEqual(2, Loading.ContentLoadInterface.GetContentFiles(Unity.Entities.Content.RuntimeContentManager.Namespace).Length); + var contentFilesNumberAfterLoadingScene = Loading.ContentLoadInterface.GetContentFiles(Unity.Entities.Content.RuntimeContentManager.Namespace).Length; #endif SceneSystem.UnloadScene(worldA.Unmanaged, worldAScene); @@ -203,7 +202,8 @@ public IEnumerator LoadMultipleSubscenes_Blocking_WithAssetBundles() worldB.Update(); #if !UNITY_EDITOR - Assert.AreEqual(0, Loading.ContentLoadInterface.GetContentFiles(Unity.Entities.Content.RuntimeContentManager.Namespace).Length); + var contentFilesNumberAfterUnLoadingScene = contentFilesNumberAfterLoadingScene - initialContentFilesNumber; + Assert.AreEqual(2, contentFilesNumberAfterUnLoadingScene); #endif } } @@ -218,7 +218,6 @@ private static PostLoadCommandBuffer CreateTestProcessAfterLoadDataCommandBuffer return postLoadCommandBuffer; } - // Only works in Editor for now until we can support SubScene building with new build settings in a test [UnityTest] public IEnumerator LoadSubscene_With_PostLoadCommandBuffer([Values] bool loadAsync, [Values] bool addCommandBufferToSection) { @@ -285,7 +284,6 @@ public IEnumerator LoadSubscene_With_PostLoadCommandBuffer([Values] bool loadAsy } [Test] - [UnityPlatform(RuntimePlatform.WindowsEditor, RuntimePlatform.OSXEditor, RuntimePlatform.LinuxEditor)] public void Load_MultipleInstancesOfSameSubScene_By_Instantiating_ResolvedScene() { var postLoadCommandBuffer1 = CreateTestProcessAfterLoadDataCommandBuffer(42); @@ -293,7 +291,13 @@ public void Load_MultipleInstancesOfSameSubScene_By_Instantiating_ResolvedScene( using (var world = CreateEntityWorld("World")) { +#if UNITY_EDITOR + Assert.IsTrue(PlayModeSceneGUID.IsValid); var resolvedScene = SceneSystem.LoadSceneAsync(world.Unmanaged, PlayModeSceneGUID, new SceneSystem.LoadParameters {AutoLoad = false, Flags = SceneLoadFlags.BlockOnImport}); +#else + Assert.IsTrue(BuildSceneGUID.IsValid); + var resolvedScene = SceneSystem.LoadSceneAsync(world.Unmanaged, BuildSceneGUID, new SceneSystem.LoadParameters {AutoLoad = false, Flags = SceneLoadFlags.BlockOnImport}); +#endif world.Update(); Assert.IsTrue(world.EntityManager.HasComponent(resolvedScene)); @@ -303,13 +307,10 @@ public void Load_MultipleInstancesOfSameSubScene_By_Instantiating_ResolvedScene( Flags = SceneLoadFlags.BlockOnImport | SceneLoadFlags.BlockOnStreamIn }; - Assert.IsTrue(PlayModeSceneGUID.IsValid); - var scene1 = world.EntityManager.Instantiate(resolvedScene); world.EntityManager.AddComponentData(scene1, postLoadCommandBuffer1); SceneSystem.LoadSceneAsync(world.Unmanaged, scene1, loadParams); - var scene2 = world.EntityManager.Instantiate(resolvedScene); world.EntityManager.AddComponentData(scene2, postLoadCommandBuffer2); SceneSystem.LoadSceneAsync(world.Unmanaged, scene2, loadParams); diff --git a/Unity.Scenes.Hybrid.Tests/Test.mat b/Unity.Scenes.Hybrid.Tests/Test.mat index 7ff25f5..36fdd47 100644 --- a/Unity.Scenes.Hybrid.Tests/Test.mat +++ b/Unity.Scenes.Hybrid.Tests/Test.mat @@ -10,9 +10,9 @@ MonoBehaviour: m_Enabled: 1 m_EditorHideFlags: 0 m_Script: {fileID: 11500000, guid: d0353a89b1f911e48b9e16bdc9f2e058, type: 3} - m_Name: - m_EditorClassIdentifier: - version: 7 + m_Name: + m_EditorClassIdentifier: + version: 9 --- !u!21 &2100000 Material: serializedVersion: 8 @@ -32,8 +32,9 @@ Material: m_CustomRenderQueue: 2000 stringTagMap: RenderType: Opaque - disabledShaderPasses: [] - m_LockedProperties: + disabledShaderPasses: + - MOTIONVECTORS + m_LockedProperties: m_SavedProperties: serializedVersion: 3 m_TexEnvs: @@ -133,3 +134,4 @@ Material: - _EmissionColor: {r: 0, g: 0, b: 0, a: 1} - _SpecColor: {r: 0.19999996, g: 0.19999996, b: 0.19999996, a: 1} m_BuildTextureStacks: [] + m_AllowLocking: 1 diff --git a/Unity.Scenes/AssetObjectManifest.cs b/Unity.Scenes/AssetObjectManifest.cs index 556fe78..a4820db 100644 --- a/Unity.Scenes/AssetObjectManifest.cs +++ b/Unity.Scenes/AssetObjectManifest.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using Unity.Collections.LowLevel.Unsafe; using UnityEditor; @@ -41,4 +40,3 @@ public static unsafe void BuildManifest(Object[] objects, AssetObjectManifest ma } #endif } -#endif diff --git a/Unity.Scenes/AsyncLoadSceneOperation.cs b/Unity.Scenes/AsyncLoadSceneOperation.cs index a26e36c..4baa844 100644 --- a/Unity.Scenes/AsyncLoadSceneOperation.cs +++ b/Unity.Scenes/AsyncLoadSceneOperation.cs @@ -1,11 +1,7 @@ using System; using System.Collections.Generic; using System.IO; -#if UNITY_DOTSRUNTIME -using Unity.Runtime.IO; -#else using Unity.Entities.Content; -#endif using System.Runtime.InteropServices; using Unity.Assertions; using Unity.Collections; @@ -17,9 +13,7 @@ using Unity.Jobs; using Unity.Profiling; using UnityEngine; -#if !NET_DOTS using System.Linq; -#endif namespace Unity.Scenes { @@ -34,10 +28,7 @@ struct AsyncLoadSceneData public BlobAssetReference BlobHeader; public BlobAssetOwner BlobHeaderOwner; public Entity SceneSectionEntity; - -#if !UNITY_DOTSRUNTIME public UntypedWeakReferenceId UnityObjectRefId; -#endif #if !UNITY_DISABLE_MANAGED_COMPONENTS public PostLoadCommandBuffer PostLoadCommandBuffer; #endif @@ -60,7 +51,7 @@ public override string ToString() return $"AsyncLoadSceneJob({_ScenePath})"; } - unsafe struct FreeJob : IJob + struct FreeJob : IJob { [NativeDisableUnsafePtrRestriction] public void* Ptr; @@ -79,7 +70,7 @@ public void Execute() for (int i = 0; i < length; ++i) { var chunks = DeserializationStatus.MegaChunkInfoList[i]; - EntityComponentStore.FreeContiguousChunks((Chunk*)chunks.MegaChunkAddress, chunks.MegaChunkSize); + EntityComponentStore.FreeContiguousChunks(chunks.MegaChunkIndex, chunks.MegaChunkSize); } } @@ -99,10 +90,9 @@ public void Dispose() freeJob.FreeChunks = true; freeJob.Schedule(_ReadHandle.JobHandle); } -#if !UNITY_DOTSRUNTIME + if (_UnityObjectRefId.IsValid) RuntimeContentManager.ReleaseObjectAsync(_UnityObjectRefId); -#endif #if !UNITY_DISABLE_MANAGED_COMPONENTS _Data.PostLoadCommandBuffer?.Dispose(); @@ -118,9 +108,8 @@ struct AsyncLoadSceneJob : IJob public long FileLength; public GCHandle LoadingOperationHandle; -#if !UNITY_DOTSRUNTIME public GCHandle ObjectReferencesHandle; -#endif + public ExclusiveEntityTransaction Transaction; public NativeArray DeserializationResult; @@ -136,10 +125,9 @@ public void Execute() LoadingOperationHandle.Free(); object[] objectReferences = null; -#if !UNITY_DOTSRUNTIME objectReferences = (object[]) ObjectReferencesHandle.Target; ObjectReferencesHandle.Free(); -#endif + try { SerializeUtility.WorldDeserializationResult deserializationResult; @@ -177,11 +165,9 @@ public void Execute() ref EntityManager _EntityManager => ref _Data.EntityManager; bool _BlockUntilFullyLoaded => _Data.BlockUntilFullyLoaded; -#if !UNITY_DOTSRUNTIME UntypedWeakReferenceId _UnityObjectRefId; #if UNITY_EDITOR RuntimeContentManager.InstanceHandle _UnityObjectRefsHandle; -#endif #endif LoadingStatus _LoadingStatus; @@ -201,9 +187,7 @@ public void Execute() public AsyncLoadSceneOperation(AsyncLoadSceneData asyncLoadSceneData) { _Data = asyncLoadSceneData; -#if !UNITY_DOTSRUNTIME _UnityObjectRefId = asyncLoadSceneData.UnityObjectRefId; -#endif _LoadingStatus = LoadingStatus.NotStarted; _DeserializationResultArray = new NativeArray(1, Allocator.Persistent); } @@ -229,14 +213,13 @@ public string ErrorStatus public Exception Exception => _LoadingException; -#if !UNITY_DOTSRUNTIME public UntypedWeakReferenceId StealReferencedUnityObjects() { var tmp = _UnityObjectRefId; _UnityObjectRefId = default; return tmp; } -#endif + private void UpdateBlocking() { if (_LoadingStatus == LoadingStatus.Completed) @@ -259,7 +242,6 @@ private void UpdateBlocking() throw new InvalidOperationException("BlobHeader must be valid"); } -#if !UNITY_DOTSRUNTIME if (_UnityObjectRefId.IsValid) { #if UNITY_EDITOR @@ -270,7 +252,7 @@ private void UpdateBlocking() RuntimeContentManager.WaitForObjectCompletion(_UnityObjectRefId); #endif } -#endif + ScheduleSceneRead(); _EntityManager.EndExclusiveEntityTransaction(); PostProcessScene(); @@ -330,11 +312,14 @@ private void UpdateAsync() } } -#if !UNITY_DOTSRUNTIME if (_UnityObjectRefId.IsValid) { #if UNITY_EDITOR _UnityObjectRefsHandle = RuntimeContentManager.LoadInstanceAsync(_UnityObjectRefId); + + // Before the Runtime Content Manager was added, even in Editor with async this code path was synchronous + // And as such, Companions depend on this behaviour to work correctly, so do not change this to asynchronous without consideration + RuntimeContentManager.WaitForInstanceCompletion(_UnityObjectRefsHandle); #else RuntimeContentManager.LoadObjectAsync(_UnityObjectRefId); #endif @@ -344,10 +329,6 @@ private void UpdateAsync() { _LoadingStatus = LoadingStatus.WaitingForEntitiesLoad; } - -#else - _LoadingStatus = LoadingStatus.WaitingForEntitiesLoad; -#endif } catch (Exception e) { @@ -357,7 +338,6 @@ private void UpdateAsync() } } -#if !UNITY_DOTSRUNTIME // Once async asset bundle load is done, we can read the asset if (_LoadingStatus == LoadingStatus.WaitingForUnityObjectReferencesLoad) { @@ -368,24 +348,9 @@ private void UpdateAsync() #endif _LoadingStatus = LoadingStatus.WaitingForEntitiesLoad; } -#endif if (_LoadingStatus == LoadingStatus.WaitingForEntitiesLoad) { - // All jobs in DOTS Runtime when singlethreaded will be executed immediately - // so if we were to create a job for IO, we would block, which is a guaranteed deadlock on the web - // so we must early out until the async read has completed without waiting on the jobhandle. -#if UNITY_DOTSRUNTIME && UNITY_SINGLETHREADED_JOBS - if (_ReadHandle.Status == ReadStatus.InProgress) - return; - if (_ReadHandle.Status == ReadStatus.Failed) - { - _LoadingFailure = $"Failed to read '{_ScenePath}'"; - _LoadingStatus = LoadingStatus.Completed; - return; - } - Assert.IsTrue(_ReadHandle.Status == ReadStatus.Complete); -#endif try { _LoadingStatus = LoadingStatus.WaitingForSceneDeserialization; @@ -434,8 +399,8 @@ public void Update() void ScheduleSceneRead() { var transaction = _EntityManager.BeginExclusiveEntityTransaction(); -#if !UNITY_DOTSRUNTIME UnityEngine.Object[] objectReferences = null; + #if UNITY_EDITOR if (_UnityObjectRefsHandle.IsValid) { @@ -458,43 +423,10 @@ void ScheduleSceneRead() DeserializationResult = _DeserializationResultArray, SceneSectionEntity = _Data.SceneSectionEntity }; -#else - var loadJob = new AsyncLoadSceneJob - { - Transaction = transaction, - LoadingOperationHandle = GCHandle.Alloc(this), - DeserializationStatus = _DeserializationStatus, - BlobHeader = _Data.BlobHeader, - FileContent = _FileContent, - FileLength = _SceneSize, - DeserializationResult = _DeserializationResultArray, - SceneSectionEntity = _Data.SceneSectionEntity - }; -#endif -#if !UNITY_DOTSRUNTIME var loadJobHandle = loadJob.Schedule(JobHandle.CombineDependencies( _EntityManager.ExclusiveEntityTransactionDependency, _ReadHandle.JobHandle)); -#else - - JobHandle decompressJob = default; - if (_Data.Codec != Codec.None) - { - decompressJob = new DecompressJob() - { - Codec = _Data.Codec, - AsyncOp = _ReadHandle.mAsyncOp, - DecompressedData = _FileContent, - DecompressedDataSize = _Data.SceneSize - }.Schedule(_ReadHandle.mJobHandle); - } - - var loadJobHandle = loadJob.Schedule(JobHandle.CombineDependencies( - _EntityManager.ExclusiveEntityTransactionDependency, - _ReadHandle.JobHandle, - decompressJob)); -#endif _EntityManager.ExclusiveEntityTransactionDependency = loadJobHandle; _DeserializationStatus = default; // _DeserializationStatus is disposed by AsyncLoadSceneJob var freeJob = new FreeJob { Ptr = _FileContent, ReadCommands = _ReadCommands, ReadHandle = _ReadHandle }; @@ -505,14 +437,11 @@ void ScheduleSceneRead() _ReadHandle = default; } -#if !UNITY_DOTSRUNTIME static readonly ProfilerMarker s_PostProcessScene = new ProfilerMarker(nameof(PostProcessScene)); -#endif void PostProcessScene() { -#if !UNITY_DOTSRUNTIME using var marker = s_PostProcessScene.Auto(); -#endif + #if !UNITY_DISABLE_MANAGED_COMPONENTS if (_Data.PostLoadCommandBuffer != null) { @@ -531,23 +460,4 @@ void PostProcessScene() _EntityManager.AddSharedComponentManaged(missingSceneTag, new SceneTag { SceneEntity = _Data.SceneSectionEntity }); } } - -#if UNITY_DOTSRUNTIME - public unsafe struct DecompressJob : IJob - { - public AsyncOp AsyncOp; - internal Codec Codec; - public unsafe byte* DecompressedData; - public int DecompressedDataSize; - - public void Execute() - { - AsyncOp.GetData(out var compressedData, out var compressedDataSize); - bool result = CodecService.Decompress(Codec, compressedData, compressedDataSize, DecompressedData, DecompressedDataSize); - - if (!result) - throw new Exception("Failed to decompress using codec " + Codec.ToString()); - } - } -#endif } diff --git a/Unity.Scenes/EditorRenderData.cs b/Unity.Scenes/EditorRenderData.cs index c31409e..bcd14b0 100644 --- a/Unity.Scenes/EditorRenderData.cs +++ b/Unity.Scenes/EditorRenderData.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using UnityEngine; @@ -11,4 +10,3 @@ internal struct EditorRenderData : ISharedComponentData, IEquatable SceneCullingMask.GetHashCode(); } } -#endif diff --git a/Unity.Scenes/EditorUpdateUtility.cs b/Unity.Scenes/EditorUpdateUtility.cs index fb389d7..9f5a134 100644 --- a/Unity.Scenes/EditorUpdateUtility.cs +++ b/Unity.Scenes/EditorUpdateUtility.cs @@ -1,7 +1,5 @@ -#if !UNITY_DOTSRUNTIME using UnityEditor; using UnityEngine; -#endif namespace Unity.Scenes { diff --git a/Unity.Scenes/EntityChangeSetSerialization.cs b/Unity.Scenes/EntityChangeSetSerialization.cs index b3e0f46..1610ad2 100644 --- a/Unity.Scenes/EntityChangeSetSerialization.cs +++ b/Unity.Scenes/EntityChangeSetSerialization.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Linq; using Unity.Assertions; @@ -373,4 +372,3 @@ static int GetCompanionLinkPackedTypeIndex(NativeArray typeHa #endif } } -#endif diff --git a/Unity.Scenes/EntitySceneOptimization.cs b/Unity.Scenes/EntitySceneOptimization.cs index 79f356b..137f47c 100644 --- a/Unity.Scenes/EntitySceneOptimization.cs +++ b/Unity.Scenes/EntitySceneOptimization.cs @@ -63,5 +63,6 @@ internal static void Optimize(World world) { var systemList = DefaultWorldInitialization.GetAllSystemTypeIndices(WorldSystemFilterFlags.EntitySceneOptimizations); OptimizeInternal(world, systemList); - } } + } + } } diff --git a/Unity.Scenes/EntityScenesPaths.cs b/Unity.Scenes/EntityScenesPaths.cs index e8d5ea7..ddf2746 100644 --- a/Unity.Scenes/EntityScenesPaths.cs +++ b/Unity.Scenes/EntityScenesPaths.cs @@ -1,9 +1,7 @@ using System; using System.Collections.Generic; using Unity.Collections; -#if !NET_DOTS using System.Linq; -#endif using UnityEngine; using Hash128 = Unity.Entities.Hash128; diff --git a/Unity.Scenes/GlobalAssetObjectResolver.cs b/Unity.Scenes/GlobalAssetObjectResolver.cs index 2fd84c2..aab1f19 100644 --- a/Unity.Scenes/GlobalAssetObjectResolver.cs +++ b/Unity.Scenes/GlobalAssetObjectResolver.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System.Collections.Generic; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; @@ -143,4 +142,3 @@ public void DisposeAssetBundles() } } } -#endif diff --git a/Unity.Scenes/Hybrid/FileUtilityHybrid.cs b/Unity.Scenes/Hybrid/FileUtilityHybrid.cs index f47e6ac..18ba684 100644 --- a/Unity.Scenes/Hybrid/FileUtilityHybrid.cs +++ b/Unity.Scenes/Hybrid/FileUtilityHybrid.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME #if UNITY_ANDROID using UnityEngine.Networking; #else @@ -38,4 +37,3 @@ public static bool FileExists(string path) } } } -#endif diff --git a/Unity.Scenes/Hybrid/ReferencedUnityObjects.cs b/Unity.Scenes/Hybrid/ReferencedUnityObjects.cs index a7e6499..d1b11f2 100644 --- a/Unity.Scenes/Hybrid/ReferencedUnityObjects.cs +++ b/Unity.Scenes/Hybrid/ReferencedUnityObjects.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using UnityEngine; using UnityEngine.Scripting.APIUpdating; @@ -21,4 +20,3 @@ public class ReferencedUnityObjects : ScriptableObject public int[] CompanionObjectIndices; } } -#endif diff --git a/Unity.Scenes/Hybrid/SerializeUtilityHybrid.cs b/Unity.Scenes/Hybrid/SerializeUtilityHybrid.cs index 575e8cd..8eecdb1 100644 --- a/Unity.Scenes/Hybrid/SerializeUtilityHybrid.cs +++ b/Unity.Scenes/Hybrid/SerializeUtilityHybrid.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System.Collections.Generic; using Unity.Collections; using Unity.Entities; @@ -140,4 +139,3 @@ public static void DeserializeObjectReferences(ReferencedUnityObjects objRefs, o } } } -#endif diff --git a/Unity.Scenes/LiveConversion/LiveConversionSceneChangeTracker.cs b/Unity.Scenes/LiveConversion/LiveConversionSceneChangeTracker.cs index 5d4d1d4..cbcf81c 100644 --- a/Unity.Scenes/LiveConversion/LiveConversionSceneChangeTracker.cs +++ b/Unity.Scenes/LiveConversion/LiveConversionSceneChangeTracker.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Collections.LowLevel.Unsafe.NotBurstCompatible; @@ -137,4 +136,3 @@ internal static NativeArray SubtractArrays(NativeArray minuend, NativeA } } } -#endif diff --git a/Unity.Scenes/LiveConversionPatcher.cs b/Unity.Scenes/LiveConversionPatcher.cs index 7907335..4a042cd 100644 --- a/Unity.Scenes/LiveConversionPatcher.cs +++ b/Unity.Scenes/LiveConversionPatcher.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using Unity.Assertions; using Unity.Burst.Intrinsics; @@ -259,4 +258,3 @@ public unsafe void ApplyPatch(LiveConversionChangeSet changeSet) } } } -#endif diff --git a/Unity.Scenes/PublicEntityRef.cs b/Unity.Scenes/PublicEntityRef.cs index f05b87f..19dbc9b 100644 --- a/Unity.Scenes/PublicEntityRef.cs +++ b/Unity.Scenes/PublicEntityRef.cs @@ -40,51 +40,4 @@ public static void Add(ref DynamicBuffer buffer, PublicEntityRe buffer.Insert(i, entityref); } } - - - [InternalBufferCapacity(16)] - struct ExternalEntityRef : IBufferElementData - { - public int entityIndex; //TODO: Should be GUID - - public static bool operator<(ExternalEntityRef a, ExternalEntityRef b) - { - return a.entityIndex < b.entityIndex; - } - - public static bool operator>(ExternalEntityRef a, ExternalEntityRef b) - { - return a.entityIndex > b.entityIndex; - } - - private static unsafe int FindInsertionPoint(ref DynamicBuffer buffer, ExternalEntityRef entityref) - { - int low = 0; - int high = buffer.Length; - while (low != high) - { - int mid = (low + high) / 2; - if (entityref < buffer[mid]) - high = mid; - else - low = mid + 1; - } - - return low; - } - - public static void Add(ref DynamicBuffer buffer, ExternalEntityRef entityref) - { - int i = FindInsertionPoint(ref buffer, entityref); - buffer.Insert(i, entityref); - } - } - - struct ExternalEntityRefInfo : IComponentData - { - public Entity SceneRef; - public Hash128 SceneGUID; - public int SubSectionIndex; - public int EntityIndexStart; - } } diff --git a/Unity.Scenes/ResolveSceneReferenceSystem.cs b/Unity.Scenes/ResolveSceneReferenceSystem.cs index 51b6628..ae0cf7e 100644 --- a/Unity.Scenes/ResolveSceneReferenceSystem.cs +++ b/Unity.Scenes/ResolveSceneReferenceSystem.cs @@ -359,13 +359,9 @@ protected unsafe override void OnUpdate() { if (!requestHeader.IsCompleted) { -#if !UNITY_DOTSRUNTIME // DOTS Runtime does not support blocking on IO if((requestSceneLoaded.LoadFlags & SceneLoadFlags.BlockOnImport) == 0) return; requestHeader.Complete(); -#else - return; -#endif } using(var headerLoadResult = SceneHeaderUtility.FinishHeaderLoad(requestHeader, scene.SceneGUID, SceneSystem.SceneLoadDir)) diff --git a/Unity.Scenes/ResolveSceneSectionUtility.cs b/Unity.Scenes/ResolveSceneSectionUtility.cs index c7f4493..47d0804 100644 --- a/Unity.Scenes/ResolveSceneSectionUtility.cs +++ b/Unity.Scenes/ResolveSceneSectionUtility.cs @@ -6,10 +6,6 @@ using Unity.Entities; using Unity.Entities.Serialization; -#if UNITY_DOTSRUNTIME -using Unity.Runtime.IO; -#endif - namespace Unity.Scenes { internal struct ResolveSceneSectionArchetypes @@ -38,7 +34,6 @@ internal static ResolveSceneSectionArchetypes CreateResolveSceneSectionArchetype }; } -#if !UNITY_DOTSRUNTIME internal static bool ResolveSceneSections(EntityManager manager, Entity sceneEntity, Hash128 sceneGUID, RequestSceneLoaded requestSceneLoaded, Hash128 artifactHash, string sceneLoadDir) { var bufLen = manager.AddBuffer(sceneEntity).Length; @@ -64,7 +59,6 @@ internal static bool ResolveSceneSections(EntityManager manager, Entity sceneEnt return ResolveSceneSections(manager, sceneEntity, requestSceneLoaded, ref sceneMetaData.Value, resolveSceneSectionArchetypes, sectionPaths, headerBlobOwner); } -#endif internal static unsafe bool ResolveSceneSections(EntityManager entityManager, Entity sceneEntity, RequestSceneLoaded requestSceneLoaded, ref SceneMetaData sceneMetaData, ResolveSceneSectionArchetypes sectionArchetypes, UnsafeList sectionPaths, BlobAssetOwner headerBlobOwner) { @@ -121,7 +115,6 @@ internal static unsafe bool ResolveSceneSections(EntityManager entityManager, En return true; } -#if !UNITY_DOTSRUNTIME internal static unsafe bool ReadHeader(string sceneHeaderPath, out BlobAssetReference sceneMetaDataRef, Hash128 sceneGUID) => ReadHeader(sceneHeaderPath, out sceneMetaDataRef, sceneGUID, out _, false); @@ -167,36 +160,6 @@ internal static unsafe bool ReadHeader(string sceneHeaderPath, out BlobAssetRefe } return true; } -#else - internal static unsafe bool ReadHeader(byte* data, long length, out BlobAssetReference sceneMetaDataRef, Hash128 sceneGUID, out BlobAssetOwner headerBlobOwner) - { - headerBlobOwner = default; - - var binaryReader = new MemoryBinaryReader(data, length); - var storedVersion = binaryReader.ReadInt(); - if (storedVersion != SceneMetaDataSerializeUtility.CurrentFileFormatVersion) - { - sceneMetaDataRef = default; - Debug.LogError($"Loading Entity Scene failed because the entity header file was an old version or doesn't exist: {sceneGUID}\nNOTE: In order to load SubScenes in the player you have to use the new BuildConfiguration asset based workflow to build & run your player."); - return false; - } - - sceneMetaDataRef = binaryReader.Read(); - - //Load header blob batch - var dataSize = sceneMetaDataRef.Value.HeaderBlobAssetBatchSize; - void* blobAssetBatch = Memory.Unmanaged.Allocate(dataSize, 16, Allocator.Persistent); - - binaryReader.ReadBytes(blobAssetBatch, dataSize); - headerBlobOwner = new BlobAssetOwner(blobAssetBatch, dataSize); - - for(int i=0;i sectionMetaDataArray, EntityManager entityManager) { // Deserialize the SceneSection custom metadata diff --git a/Unity.Scenes/ResourceCatalogData.cs b/Unity.Scenes/ResourceCatalogData.cs index 3744eca..1ede510 100644 --- a/Unity.Scenes/ResourceCatalogData.cs +++ b/Unity.Scenes/ResourceCatalogData.cs @@ -2,18 +2,14 @@ using System.IO; using Unity.Collections; using Unity.Entities; -#if !UNITY_DOTSRUNTIME using UnityEngine.Scripting.APIUpdating; -#endif namespace Unity.Scenes { /// /// Information for resources to be loaded at runtime. /// -#if !UNITY_DOTSRUNTIME [MovedFrom(true, "Unity.Entities.Hybrid", "Unity.Entities.Hybrid")] -#endif public struct ResourceMetaData { /// @@ -36,29 +32,6 @@ public enum Type /// public Hash128 ResourceId; -#if UNITY_DOTSRUNTIME - /// - /// For scenes, if AutoLoad is true, the scene will be loaded when the player starts. - /// - [Flags] - public enum Flags - { - /// - /// Doesn't automatically load the scene. - /// - None = 0, - /// - /// Loads the scene when the player starts. - /// - AutoLoad = 1 - } - - /// - /// Flags to control the behavior of the asset. - /// - public Flags ResourceFlags; -#endif - /// /// The type of resource. /// @@ -68,9 +41,7 @@ public enum Flags /// /// Container for resource data. /// -#if !UNITY_DOTSRUNTIME [MovedFrom(true, "Unity.Entities.Hybrid", "Unity.Entities.Hybrid")] -#endif public struct ResourceCatalogData { /// @@ -108,15 +79,9 @@ public Hash128 GetGUIDFromPath(string path) Debug.LogWarning("Deprecation Warning - Use of GetGUIDFromPath working without extensions is obsolete. (RemovedAfter 2021-02-05)"); return resources[i].ResourceId; } -#if !NET_DOTS + var currentPathWithoutExtensionLower = currentPathWithoutExtension.ToLower(); var pathLower = path.ToLower(); -#else - // NET_DOTS doesn't have toLower so we just make sure we export lower case data - // as the user is already expected to pass in lowercase strings - var currentPathWithoutExtensionLower = currentPathWithoutExtension; - var pathLower = path; -#endif if (pathLower == currentPathWithoutExtensionLower) { Debug.LogWarning("Deprecation Warning - Use of GetGUIDFromPath working with lower-case paths is obsolete. (RemovedAfter 2021-02-05)"); diff --git a/Unity.Scenes/Scene.cs b/Unity.Scenes/Scene.cs deleted file mode 100644 index 031658b..0000000 --- a/Unity.Scenes/Scene.cs +++ /dev/null @@ -1,11 +0,0 @@ -#if UNITY_DOTSRUNTIME -namespace Unity.Scenes -{ - /// DOTS representation of a Scene mirroring UnityEngine's Scene representation - internal struct Scene - { - public bool isLoaded; - public bool IsValid() { return false; } - } -} -#endif diff --git a/Unity.Scenes/Scene.cs.meta b/Unity.Scenes/Scene.cs.meta deleted file mode 100644 index 26b5cd4..0000000 --- a/Unity.Scenes/Scene.cs.meta +++ /dev/null @@ -1,11 +0,0 @@ -fileFormatVersion: 2 -guid: 7de74577cf5dbca428b2d5878bceef33 -MonoImporter: - externalObjects: {} - serializedVersion: 2 - defaultReferences: [] - executionOrder: 0 - icon: {instanceID: 0} - userData: - assetBundleName: - assetBundleVariant: diff --git a/Unity.Scenes/SceneBoundingVolume.cs b/Unity.Scenes/SceneBoundingVolume.cs index 176835d..49cdcb6 100644 --- a/Unity.Scenes/SceneBoundingVolume.cs +++ b/Unity.Scenes/SceneBoundingVolume.cs @@ -1,17 +1,13 @@ using Unity.Entities; using Unity.Mathematics; -#if !UNITY_DOTSRUNTIME using UnityEngine.Scripting.APIUpdating; -#endif namespace Unity.Scenes { /// /// Component that represents a Scene or a Scene Section bounding volume /// -#if !UNITY_DOTSRUNTIME [MovedFrom(true, "Unity.Entities", "Unity.Entities.Hybrid")] -#endif public struct SceneBoundingVolume : IComponentData { /// diff --git a/Unity.Scenes/SceneHeaderUtility.cs b/Unity.Scenes/SceneHeaderUtility.cs index 591f78b..e92d847 100644 --- a/Unity.Scenes/SceneHeaderUtility.cs +++ b/Unity.Scenes/SceneHeaderUtility.cs @@ -12,39 +12,10 @@ using UnityEngine; using Hash128 = Unity.Entities.Hash128; -#if UNITY_DOTSRUNTIME -using Unity.Runtime.IO; -#endif - namespace Unity.Scenes { internal unsafe struct RequestSceneHeader : ICleanupComponentData { -#if UNITY_DOTSRUNTIME - // DOTS Runtime IO handle. When the AsyncReadManager provides a mechanism to read files without knowing the size - // this type can be changed to a common type. - public int IOHandle; - public bool IsCompleted - { - get - { - return new AsyncOp() {m_Handle = IOHandle}.GetStatus() > AsyncOp.Status.InProgress; - } - } - - public bool Succeeded => new AsyncOp() {m_Handle = IOHandle}.GetStatus() == AsyncOp.Status.Success; - - public void GetFileData(out byte* data, out long fileSize) - { - new AsyncOp() {m_Handle = IOHandle}.GetData(out data, out var intFileSize); - fileSize = intFileSize; - } - - public void Dispose() - { - new AsyncOp() {m_Handle = IOHandle}.Dispose(); - } -#else public SceneHeaderUtility.HeaderData* HeaderData; public bool IsCompleted => HeaderData->JobHandle.IsCompleted; @@ -63,7 +34,6 @@ public void Dispose() Memory.Unmanaged.Free(headerData->Data, Allocator.Persistent); Memory.Unmanaged.Free(headerData, Allocator.Persistent); } -#endif } internal struct SceneHeaderUtility @@ -73,14 +43,10 @@ internal struct SceneHeaderUtility EntityQuery _CleanupQueryWithoutRequestSceneLoaded; EntityQuery _CleanupQueryWithDisableSceneResolveAndLoad; EntityQuery _CleanupQueryWithDisabled; -#if !UNITY_DOTSRUNTIME private NativeList _PendingCleanups; -#endif public SceneHeaderUtility(SystemBase system) { -#if !UNITY_DOTSRUNTIME _PendingCleanups = new NativeList(0, Allocator.Persistent); -#endif _CleanupQueryWithoutSceneReference = new EntityQueryBuilder(Allocator.Temp) .WithAll().WithNone().Build(system); _CleanupQueryWithoutResolvedSceneHash = new EntityQueryBuilder(Allocator.Temp) @@ -96,19 +62,16 @@ public SceneHeaderUtility(SystemBase system) public void Dispose(EntityManager entityManager) { CleanupHeaders(entityManager); -#if !UNITY_DOTSRUNTIME for (int i = 0; i < _PendingCleanups.Length; ++i) { _PendingCleanups[i].Complete(); _PendingCleanups[i].Dispose(); } _PendingCleanups.Dispose(); -#endif } public void CleanupHeaders(EntityManager entityManager) { -#if !UNITY_DOTSRUNTIME for (int i=0, length=_PendingCleanups.Length; iHeaderLoadResult; @@ -184,50 +142,6 @@ internal static unsafe HeaderLoadResult FinishHeaderLoad(RequestSceneHeader requ LogHeaderLoadError(loadResult.Status, sceneGUID); return loadResult; } -#else - internal static unsafe HeaderLoadResult FinishHeaderLoad(RequestSceneHeader requestHeader, Hash128 sceneGUID, string sceneLoadDir) - { - var headerLoadResult = new HeaderLoadResult(); - if(requestHeader.Succeeded) - { - headerLoadResult.Status = HeaderLoadStatus.MissingFile; - } - - requestHeader.GetFileData(out var headerData, out var headerSize); - - if(!BlobAssetReference.TryReadInplace(headerData, headerSize, SceneMetaDataSerializeUtility.CurrentFileFormatVersion, out var sceneMetaDataBlobRef, out var numBytesRead)) - { - headerLoadResult.Status = HeaderLoadStatus.WrongVersion; - return headerLoadResult; - } - - headerLoadResult.Status = HeaderLoadStatus.Success; - - headerLoadResult.SceneMetaData = sceneMetaDataBlobRef; - ref var sceneMetaData = ref sceneMetaDataBlobRef.Value; - - //Load header blob batch - var dataSize = sceneMetaData.HeaderBlobAssetBatchSize; - void* blobAssetBatch = Memory.Unmanaged.Allocate(dataSize, 16, Allocator.Persistent); - UnsafeUtility.MemCpy(blobAssetBatch, headerData + numBytesRead, dataSize); - - var headerBlobOwner = new BlobAssetOwner(blobAssetBatch, dataSize); - - var sectionCount = sceneMetaData.Sections.Length; - for (int i = 0; i < sectionCount; ++i) - { - sceneMetaData.Sections[i].BlobHeader = - sceneMetaData.Sections[i].BlobHeader.Resolve(headerBlobOwner); - } - - headerLoadResult.HeaderBlobOwner = headerBlobOwner; - headerLoadResult.SectionPaths = new UnsafeList(sectionCount, Allocator.TempJob); - BuildSectionPathsForContentArchives(ref headerLoadResult.SectionPaths, ref sceneMetaData, sceneGUID, sceneLoadDir); - return headerLoadResult; - } -#endif - -#if !UNITY_DOTSRUNTIME internal class ManagedHeaderData { @@ -295,7 +209,7 @@ public void Execute() } headerLoadResult.HeaderBlobOwner = headerBlobOwner; - headerLoadResult.SectionPaths = new UnsafeList(sectionCount, Allocator.TempJob); + headerLoadResult.SectionPaths = new UnsafeList(sectionCount, Allocator.Persistent); #if UNITY_EDITOR var managedHeaderData = (ManagedHeaderData)HeaderData->ManagedHeaderData.Target; BuildSectionPaths(ref headerLoadResult.SectionPaths, ref sceneMetaData, SceneGUID, managedHeaderData.Paths, ArtifactHash); @@ -304,15 +218,9 @@ public void Execute() #endif } } -#endif internal static unsafe RequestSceneHeader CreateRequestSceneHeader(Hash128 sceneGUID, RequestSceneLoaded requestSceneLoaded, Hash128 artifactHash, string sceneLoadDir) { -#if UNITY_DOTSRUNTIME - var sceneHeaderPath = EntityScenesPaths.FullPathForFile(sceneLoadDir, EntityScenesPaths.RelativePathForSceneFile(sceneGUID, EntityScenesPaths.PathType.EntitiesHeader, -1)); - var iohandle = IOService.RequestAsyncRead(sceneHeaderPath).m_Handle; - return new RequestSceneHeader {IOHandle = iohandle}; -#else var headerData = (HeaderData*)Memory.Unmanaged.Allocate(sizeof(HeaderData), 16, Allocator.Persistent); *headerData = default; @@ -348,7 +256,6 @@ internal static unsafe RequestSceneHeader CreateRequestSceneHeader(Hash128 scene }.Schedule(handle.JobHandle); return new RequestSceneHeader {HeaderData = headerData}; -#endif } public static void ScheduleHeaderLoadOnEntity(EntityManager EntityManager, Entity sceneEntity, Hash128 sceneGUID, RequestSceneLoaded requestSceneLoaded, Hash128 artifactHash, string sceneLoadDir) diff --git a/Unity.Scenes/SceneSectionReferencedUnityObjects.cs b/Unity.Scenes/SceneSectionReferencedUnityObjects.cs index 89703c4..d79e2d4 100644 --- a/Unity.Scenes/SceneSectionReferencedUnityObjects.cs +++ b/Unity.Scenes/SceneSectionReferencedUnityObjects.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using Unity.Entities; using Unity.Entities.Serialization; @@ -27,5 +26,4 @@ public SceneSectionReferencedUnityObjects(UntypedWeakReferenceId bundles) public override int GetHashCode() => _sceneBundleHandles.GetHashCode(); } } -#endif diff --git a/Unity.Scenes/SceneSectionStreamingSystem.cs b/Unity.Scenes/SceneSectionStreamingSystem.cs index 0e45275..3635127 100644 --- a/Unity.Scenes/SceneSectionStreamingSystem.cs +++ b/Unity.Scenes/SceneSectionStreamingSystem.cs @@ -1,11 +1,10 @@ using System; +using System.Collections.Generic; using Unity.Burst; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Entities; -#if !UNITY_DOTSRUNTIME using Unity.Entities.Content; -#endif using Unity.Mathematics; using Unity.Profiling; using UnityEngine; @@ -87,9 +86,7 @@ struct Stream readonly ProfilerMarker s_MoveEntitiesFrom = new ProfilerMarker("SceneStreaming.MoveEntitiesFrom"); readonly ProfilerMarker s_ExtractEntityRemapRefs = new ProfilerMarker("SceneStreaming.ExtractEntityRemapRefs"); readonly ProfilerMarker s_AddSceneSharedComponents = new ProfilerMarker("SceneStreaming.AddSceneSharedComponents"); -#if !UNITY_DOTSRUNTIME static readonly ProfilerMarker s_UnloadSectionImmediate = new ProfilerMarker("SceneStreaming." + nameof(UnloadSectionImmediate)); -#endif readonly ProfilerMarker s_MoveEntities = new ProfilerMarker("SceneStreaming." + nameof(MoveEntities)); readonly ProfilerMarker s_UpdateLoadOperation = new ProfilerMarker("SceneStreaming." + nameof(UpdateLoadOperation)); @@ -248,42 +245,26 @@ static internal void AddStreamingWorldSystems(World world) group.SortSystems(); } - static unsafe NativeArray GetExternalRefEntities(EntityManager manager, AllocatorManager.AllocatorHandle allocator) - { - var type = ComponentType.ReadOnly(); - using (var group = manager.CreateEntityQuery(&type, 1)) - { - return group.ToEntityArray(allocator); - } - } - - static NativeArray ExternalRefToSceneTag(NativeArray externalEntityRefInfos, Entity sceneEntity, EntityQuery sectionDataQuery, AllocatorManager.AllocatorHandle allocator) + static SceneTag SceneTagForSection0(Entity sceneEntity, EntityQuery sectionDataQuery) { - // Todo: When NativeArray supports custom allocators, remove these .ToAllocator callsites DOTS-7695 - var sceneTags = new NativeArray(externalEntityRefInfos.Length, allocator.ToAllocator); + using var sectionDataEntities = sectionDataQuery.ToEntityArray(Allocator.Temp); + using var sectionData = sectionDataQuery.ToComponentDataArray(Allocator.Temp); + using var sceneEntityReference = sectionDataQuery.ToComponentDataArray(Allocator.Temp); - using (var sectionDataEntities = sectionDataQuery.ToEntityArray(Allocator.TempJob)) - using (var sectionData = sectionDataQuery.ToComponentDataArray(Allocator.TempJob)) - using (var sceneEntityReference = sectionDataQuery.ToComponentDataArray(Allocator.TempJob)) + for (int i = 0; i < sectionData.Length; ++i) { - for (int i = 0; i < sectionData.Length; ++i) + // Multiple instances of the same scene can co-exist, they need to be differentiated. + // Searching for the scene GUID instead of the scene entity would be ambiguous. + if ( + sceneEntity == sceneEntityReference[i].SceneEntity && + sectionData[i].SubSectionIndex == 0 + ) { - for (int j = 0; j < externalEntityRefInfos.Length; ++j) - { - if ( - sceneEntity == sceneEntityReference[i].SceneEntity && - externalEntityRefInfos[j].SceneGUID == sectionData[i].SceneGUID && - externalEntityRefInfos[j].SubSectionIndex == sectionData[i].SubSectionIndex - ) - { - sceneTags[j] = new SceneTag { SceneEntity = sectionDataEntities[i] }; - break; - } - } + return new SceneTag { SceneEntity = sectionDataEntities[i] }; } } - return sceneTags; + throw new InvalidOperationException("Could not find the scene entity corresponding to the section being loaded!"); } struct EntityRemapArgs @@ -293,6 +274,7 @@ struct EntityRemapArgs public Entity SceneEntity; public EntityQuery SectionData; public EntityQuery PublicRefFilter; + public SceneSectionData SceneSectionData; public NativeArray OutEntityRemapping; } @@ -306,6 +288,7 @@ bool MoveEntities(EntityManager srcManager, Entity sectionEntity, ref Entity pre NativeArray entityRemapping; using (s_ExtractEntityRemapRefs.Auto()) { + var sceneSectionData = SystemAPI.GetComponent(sectionEntity); var remapArgs = new EntityRemapArgs { EntityManager = EntityManager, @@ -313,6 +296,7 @@ bool MoveEntities(EntityManager srcManager, Entity sectionEntity, ref Entity pre PublicRefFilter = m_PublicRefFilter, SrcManager = srcManager, SceneEntity = sceneEntity, + SceneSectionData = sceneSectionData }; if (!ExtractEntityRemapRefs(ref remapArgs)) return false; @@ -353,12 +337,6 @@ bool MoveEntities(EntityManager srcManager, Entity sectionEntity, ref Entity pre } } - var sharedComponentFilter = new SceneTag {SceneEntity = sectionEntity}; - Entities.WithSharedComponentFilter(sharedComponentFilter).ForEach((ref ExternalEntityRefInfo sceneRef) => - { - sceneRef.SceneRef = sceneEntity; - }).Run(); - if (prefabRoot != Entity.Null) prefabRoot = EntityRemapUtility.RemapEntity(ref entityRemapping, prefabRoot); @@ -368,68 +346,75 @@ bool MoveEntities(EntityManager srcManager, Entity sectionEntity, ref Entity pre return true; } - [BurstCompile] + // The Burst compilation of the following function causes ECSB-621 + // It can be re-enabled once UUM-46084 (fix for the root cause) has landed + // [BurstCompile] static bool ExtractEntityRemapRefs(ref EntityRemapArgs args) { - // External entity references are "virtual" entities. If we don't have any, only real entities need remapping - int remapTableSize = args.SrcManager.EntityCapacity; + if (args.SceneSectionData.SubSectionIndex == 0) + { + // External entity references are "virtual" entities. If we don't have any, only real entities need remapping + // Section 0 doesn't have an external ref info, let's skip the whole process. + args.OutEntityRemapping = new NativeArray(args.SrcManager.EntityCapacity, Allocator.TempJob); + return true; + } + + // Loading a section which isn't section 0. We need to figure out the exact count of entities in the section, + // because immediately after that comes the external references that need remapping. + // Above we could use EntityCapacity as an approximation, it's faster than going over the archetypes to get the exact count. + int entitiesCount = 0; - using (var externalRefEntities = GetExternalRefEntities(args.SrcManager, Allocator.TempJob)) + unsafe { - // We can potentially have several external entity reference arrays, each one pointing to a different scene - var externalEntityRefInfos = new NativeArray(externalRefEntities.Length, Allocator.Temp); - for (int i = 0; i < externalRefEntities.Length; ++i) + var access = args.SrcManager.GetCheckedEntityDataAccess(); + var archetypes = access->EntityComponentStore->m_Archetypes; + for (int ai = 0, ac = archetypes.Length; ai < ac; ai++) { - // External references point to indices beyond the range used by the entities in this scene - // The highest index used by all those references defines how big the remap table has to be - externalEntityRefInfos[i] = args.SrcManager.GetComponentData(externalRefEntities[i]); - var extRefs = args.SrcManager.GetBuffer(externalRefEntities[i]); - remapTableSize = math.max(remapTableSize, externalEntityRefInfos[i].EntityIndexStart + extRefs.Length); + entitiesCount += archetypes[ai]->EntityCount; } + } - // Within a scene, external scenes are identified by some ID - // In the destination world, scenes are identified by an entity - // Every entity coming from a scene needs to have a SceneTag that references the scene entity - using (var sceneTags = ExternalRefToSceneTag(externalEntityRefInfos, args.SceneEntity, args.SectionData, Allocator.TempJob)) - { - args.OutEntityRemapping = new NativeArray(remapTableSize, Allocator.TempJob); + int remapTableSize = entitiesCount; - for (int i = 0; i < externalRefEntities.Length; ++i) - { - var extRefs = args.SrcManager.GetBuffer(externalRefEntities[i]); - var extRefInfo = args.SrcManager.GetComponentData(externalRefEntities[i]); + // Within a scene, external scenes are identified by some ID + // In the destination world, scenes are identified by an entity + // Every entity coming from a scene needs to have a SceneTag that references the scene entity + var sceneTag = SceneTagForSection0(args.SceneEntity, args.SectionData); - // A scene that external references point to is expected to have a single public reference array - args.PublicRefFilter.SetSharedComponentFilter(sceneTags[i]); - using (var pubRefEntities = args.PublicRefFilter.ToEntityArray(Allocator.TempJob)) - { - if (pubRefEntities.Length == 0) - { - // If the array is missing, the external scene isn't loaded, we have to wait. - args.OutEntityRemapping.Dispose(); - return false; - } + // A scene section 0 is expected to have a single public reference array + args.PublicRefFilter.SetSharedComponentFilter(sceneTag); + var pubRefEntities = args.PublicRefFilter.ToEntityArray(Allocator.Temp); - var pubRefs = args.EntityManager.GetBuffer(pubRefEntities[0]); + if (pubRefEntities.Length == 0) + { + // If the array is missing, the scene section 0 isn't loaded, we have to wait. + args.OutEntityRemapping.Dispose(); + return false; + } - // Proper mapping from external reference in section to entity in main world - for (int k = 0; k < extRefs.Length; ++k) - { - var srcIdx = extRefInfo.EntityIndexStart + k; - var target = pubRefs[extRefs[k].entityIndex].targetEntity; + if (pubRefEntities.Length > 1) + { + throw new InvalidOperationException("Multiple entities containing public references arrays found in scene section 0!"); + } - // External references always have a version number of 1 - args.OutEntityRemapping[srcIdx] = new EntityRemapUtility.EntityRemapInfo - { - SourceVersion = 1, - Target = target - }; - } - } + var pubRefs = args.EntityManager.GetBuffer(pubRefEntities[0]); - args.PublicRefFilter.ResetFilter(); - } - } + // Space is required to handle every possible external reference that needs remapping. + remapTableSize += pubRefs.Length; + args.OutEntityRemapping = new NativeArray(remapTableSize, Allocator.TempJob); + + // Proper mapping from external reference in section to entity in main world + for (int k = 0; k < pubRefs.Length; ++k) + { + var srcIdx = entitiesCount + k; + var target = pubRefs[k].targetEntity; + + // External references always have a version number of 1 (like any other entity in a deserialized file) + args.OutEntityRemapping[srcIdx] = new EntityRemapUtility.EntityRemapInfo + { + SourceVersion = 1, + Target = target + }; } return true; @@ -544,14 +529,13 @@ unsafe UpdateLoadOperationResult UpdateLoadOperation( EntityManager.AddComponentData(sectionEntity, default(IsSectionLoaded)); } -#if !UNITY_DOTSRUNTIME var objRefs = operation.StealReferencedUnityObjects(); if (objRefs.IsValid) { EntityManager.AddSharedComponentManaged(sectionEntity, new SceneSectionReferencedUnityObjects(objRefs)); RuntimeContentManager.ReleaseObjectAsync(objRefs); } -#endif + if (prefabRoot != Entity.Null) { var sceneEntity = EntityManager.GetComponentData(sectionEntity).SceneEntity; @@ -834,9 +818,7 @@ internal static bool CheckDependantSectionsLoaded(EntityManager entityManager, E internal static void UnloadSectionImmediate(WorldUnmanaged world, Entity scene) { -#if !UNITY_DOTSRUNTIME using var marker = s_UnloadSectionImmediate.Auto(); -#endif ref var singleton = ref world.GetExistingSystemState().GetSingletonEntityQueryInternal( ComponentType.ReadWrite()) @@ -866,10 +848,9 @@ ref world.GetExistingSystemState().GetSingletonEnti world.EntityManager.RemoveComponent(scene); world.EntityManager.RemoveComponent(scene); } -#if !UNITY_DOTSRUNTIME + if (world.EntityManager.HasComponent(scene)) world.EntityManager.RemoveComponent(scene); -#endif } int CreateAsyncLoadScene(Entity entity, bool blockUntilFullyLoaded) @@ -926,9 +907,7 @@ AsyncLoadSceneOperation CreateAsyncLoadSceneOperation(EntityManager dstManager, BlobHeader = sceneData.BlobHeader, BlobHeaderOwner = blobHeaderOwner, SceneSectionEntity = entity, -#if !UNITY_DOTSRUNTIME UnityObjectRefId = sectionData.HybridReferenceId, -#endif #if !UNITY_DISABLE_MANAGED_COMPONENTS PostLoadCommandBuffer = postLoadCommandBuffer #endif diff --git a/Unity.Scenes/SceneSystem.cs b/Unity.Scenes/SceneSystem.cs index e30d5d5..1a2cbf7 100644 --- a/Unity.Scenes/SceneSystem.cs +++ b/Unity.Scenes/SceneSystem.cs @@ -6,9 +6,7 @@ using Unity.Entities; using Unity.Entities.Serialization; using UnityEngine; -#if !UNITY_DOTSRUNTIME using UnityEngine.SceneManagement; -#endif using Hash128 = Unity.Entities.Hash128; namespace Unity.Scenes @@ -49,12 +47,10 @@ public bool AutoLoad /// The flags applied when loading the scene. /// public SceneLoadFlags Flags; -#if !UNITY_DOTSRUNTIME /// /// The priority of the load operation. /// public int Priority; -#endif } static internal RequestSceneLoaded CreateRequestSceneLoaded(LoadParameters loadParameters) @@ -67,22 +63,7 @@ static internal RequestSceneLoaded CreateRequestSceneLoaded(LoadParameters loadP private EntityQuery _unloadSceneQuery; BlobAssetReference catalogData; - -#if UNITY_DOTSRUNTIME - internal void SetCatalogData(BlobAssetReference newCatalogData) - { - catalogData = newCatalogData; - } -#endif - - - static internal string SceneLoadDir => -#if UNITY_DOTSRUNTIME - "Data"; -#else - Application.streamingAssetsPath; - -#endif + static internal string SceneLoadDir => Application.streamingAssetsPath; /// /// Callback invoked when the system starts running. @@ -133,7 +114,6 @@ public void OnDestroy(ref SystemState state) void LoadCatalogData(ref SystemState state) { -#if !UNITY_DOTSRUNTIME var fullSceneInfoPath = EntityScenesPaths.FullPathForFile(SceneLoadDir, EntityScenesPaths.RelativePathForSceneInfoFile); if (FileUtilityHybrid.FileExists(fullSceneInfoPath)) { @@ -143,7 +123,6 @@ void LoadCatalogData(ref SystemState state) return; } } -#endif } /// @@ -642,7 +621,6 @@ public static Entity GetSceneEntity(WorldUnmanaged world, Hash128 sceneGUID) return sceneEntity; } -#if !UNITY_DOTSRUNTIME /// /// Find the scene given a guid and no DisableLiveConversion component. This will only return the first matching scene. /// @@ -667,7 +645,6 @@ internal static Entity GetLiveConvertedSceneEntity(ref SystemState state, Hash12 return sceneEntity; } -#endif /// /// Callback invoked when the system is updated. diff --git a/Unity.Scenes/SubScene.cs b/Unity.Scenes/SubScene.cs index d5fffa8..b8920f5 100644 --- a/Unity.Scenes/SubScene.cs +++ b/Unity.Scenes/SubScene.cs @@ -1,4 +1,3 @@ -#if !UNITY_DOTSRUNTIME using System; using System.Collections.Generic; using Unity.Entities; @@ -415,4 +414,3 @@ private void OnDestroy() } } } -#endif diff --git a/Unity.Transforms.Hybrid/CompanionGameObjectUpdateTransformSystem.cs b/Unity.Transforms.Hybrid/CompanionGameObjectUpdateTransformSystem.cs index 9833144..ca94bcd 100644 --- a/Unity.Transforms.Hybrid/CompanionGameObjectUpdateTransformSystem.cs +++ b/Unity.Transforms.Hybrid/CompanionGameObjectUpdateTransformSystem.cs @@ -16,7 +16,7 @@ struct CompanionGameObjectUpdateTransformCleanup : ICleanupComponentData [WorldSystemFilter(WorldSystemFilterFlags.Default | WorldSystemFilterFlags.Editor)] [UpdateAfter(typeof(TransformSystemGroup))] [BurstCompile] - partial class CompanionGameObjectUpdateTransformSystem : SystemBase + public partial class CompanionGameObjectUpdateTransformSystem : SystemBase { readonly ProfilerMarker s_ProfilerMarkerAddNew = new("AddNew"); readonly ProfilerMarker s_ProfilerMarkerRemove = new("Remove"); diff --git a/Unity.Transforms.Hybrid/Unity.Transforms.Hybrid.asmdef b/Unity.Transforms.Hybrid/Unity.Transforms.Hybrid.asmdef index 21f37ad..8a49e91 100644 --- a/Unity.Transforms.Hybrid/Unity.Transforms.Hybrid.asmdef +++ b/Unity.Transforms.Hybrid/Unity.Transforms.Hybrid.asmdef @@ -15,9 +15,7 @@ "overrideReferences": false, "precompiledReferences": [], "autoReferenced": true, - "defineConstraints": [ - "!UNITY_DOTSRUNTIME" - ], + "defineConstraints": [], "versionDefines": [], "noEngineReferences": false } diff --git a/Unity.Transforms.Tests/TransformDataTests.cs b/Unity.Transforms.Tests/TransformDataTests.cs index fa3109b..dcd8b75 100644 --- a/Unity.Transforms.Tests/TransformDataTests.cs +++ b/Unity.Transforms.Tests/TransformDataTests.cs @@ -36,6 +36,11 @@ static bool AreNearlyEqual(float4x4 a, float4x4 b, float tolerance) return AreNearlyEqual(a.c0, b.c0, tolerance) && AreNearlyEqual(a.c1, b.c1, tolerance) && AreNearlyEqual(a.c2, b.c2, tolerance) && AreNearlyEqual(a.c3, b.c3, tolerance); } + static bool AreNearlyEqual(LocalTransform a, LocalTransform b, float tolerance) + { + return AreNearlyEqual(a.Position, b.Position, tolerance) && AreNearlyEqual(a.Rotation.value, b.Rotation.value, tolerance) && AreNearlyEqual(a.Scale, b.Scale, tolerance); + } + static LocalTransform GetTestTransform1() { var rotation = quaternion.Euler(math.PI / 2, math.PI / 3, math.PI / 4); @@ -234,25 +239,41 @@ public void TDT_ToInverseMatrix() Assert.IsTrue(AreNearlyEqual(transform.Inverse().ToMatrix(), math.inverse(matrix), k_Tolerance)); } - [Test] - public void TDT_FromMatrix() + static readonly TestCaseData[] k_FromMatrixTestCaseData = { - var matrix = GetTestMatrix1(); - var transform = LocalTransform.FromMatrix(matrix); - Assert.IsTrue(AreNearlyEqual(transform.ToMatrix(), matrix, k_Tolerance)); + new (float4x4.identity, new LocalTransform() { Position = float3.zero, Rotation = quaternion.identity, Scale = 1.0f }), + new (float4x4.TRS(float3.zero, quaternion.identity, 2.0f), new LocalTransform() { Position = float3.zero, Rotation = quaternion.identity, Scale = 2.0f }), + new (float4x4.TRS(new float3(1.0f, 2.0f, 3.0f), quaternion.EulerXYZ(math.radians(new float3(10.0f, 20f, -45f))), 2.0f), new LocalTransform() { Position = new float3(1.0f, 2.0f, 3.0f), Rotation = quaternion.EulerXYZ(math.radians(new float3(10.0f, 20f, -45f))), Scale = 2.0f }), + new (new float4x4(2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f), new LocalTransform() { Position = float3.zero, Rotation = quaternion.identity, Scale = 2.0f }), + new (new float4x4(1.0f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 2.0f, 0.0f, 0.0f, 1.0f, 3.0f, 0.0f, 0.0f, 0.0f, 1.0f), new LocalTransform() { Position = new float3(1.0f, 2.0f, 3.0f), Rotation = quaternion.identity, Scale = 1.0f }), + new (new float4x4(0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 0.0f, 2.0f, -1.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 1.0f), new LocalTransform() { Position = new float3(1.0f, 2.0f, 3.0f), Rotation = quaternion.RotateY(math.radians(90.0f)), Scale = 1.0f }), + }; + + static readonly TestCaseData[] k_FromMatrixInvalidTestCaseData = + { + new (new float4x4(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)), // Non-scaled, skewed + new (new float4x4(1.0f, 0.0f, 0.0f, 0.0f, 1.0f, 2.0f, 0.0f, 0.0f, 1.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)), // Non-uniform scaled, skewed + new (new float4x4(1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 2.0f, 0.0f, 0.0f, 0.0f, 0.0f, 3.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f)), // Non-uniform scaled, orthogonal + }; + + [TestCaseSource(nameof(k_FromMatrixTestCaseData))] + public void TDT_FromMatrixSafe(float4x4 input, LocalTransform expected) + { + LocalTransform localTransform = LocalTransform.FromMatrixSafe(input); + Assert.IsTrue(AreNearlyEqual(localTransform, expected, k_Tolerance)); } - [Test] - public void TDT_FromMatrixSafe() + [TestCaseSource(nameof(k_FromMatrixInvalidTestCaseData))] + public void TDT_FromMatrixSafe_Throws(float4x4 input) { - var matrix = GetTestMatrix1(); - FastAssert.DoesNotThrow(() => { LocalTransform.FromMatrix(matrix); }); - var nonuniformScaleMatrix = matrix; - nonuniformScaleMatrix.c0 *= .5f; - FastAssert.Throws(() => { LocalTransform.FromMatrixSafe(nonuniformScaleMatrix); }); - var shearMatrix = matrix; - shearMatrix.c0 = shearMatrix.c1; - FastAssert.Throws(() => { LocalTransform.FromMatrixSafe(shearMatrix); }); + NUnit.Framework.Assert.Throws( () => LocalTransform.FromMatrixSafe(input)); + } + + [TestCaseSource(nameof(k_FromMatrixTestCaseData))] + public void TDT_FromMatrix(float4x4 input, LocalTransform expected) + { + LocalTransform localTransform = LocalTransform.FromMatrix(input); + Assert.IsTrue(AreNearlyEqual(localTransform, expected, k_Tolerance)); } [Test] diff --git a/Unity.Transforms.Tests/TransformTests.cs b/Unity.Transforms.Tests/TransformTests.cs index 2115287..8b2b4b5 100644 --- a/Unity.Transforms.Tests/TransformTests.cs +++ b/Unity.Transforms.Tests/TransformTests.cs @@ -13,18 +13,16 @@ partial class TransformTests : ECSTestsFixture const float k_Tolerance = 0.01f; // Helpers to compare matrices for nearly-equality + // Note: see also functions in TransformDataTests.cs static bool AreNearlyEqual(float a, float b, float tolerance) { return math.abs(a - b) <= tolerance; } + static bool AreNearlyEqual(float4 a, float4 b, float tolerance) { return AreNearlyEqual(a.x, b.x, tolerance) && AreNearlyEqual(a.y, b.y, tolerance) && AreNearlyEqual(a.z, b.z, tolerance) && AreNearlyEqual(a.w, b.w, tolerance); } - static bool AreNearlyEqual(float4x4 a, float4x4 b, float tolerance) - { - return AreNearlyEqual(a.c0, b.c0, tolerance) && AreNearlyEqual(a.c1, b.c1, tolerance) && AreNearlyEqual(a.c2, b.c2, tolerance) && AreNearlyEqual(a.c3, b.c3, tolerance); - } [Test] public void TRS_ChildPosition() @@ -33,14 +31,14 @@ public void TRS_ChildPosition() var child = m_Manager.CreateEntity(typeof(LocalToWorld), typeof(LocalTransform), typeof(Parent)); m_Manager.SetComponentData(parent, LocalTransform.Identity); - m_Manager.SetComponentData(child, new Parent {Value = parent}); + m_Manager.SetComponentData(child, new Parent { Value = parent }); m_Manager.SetComponentData(child, LocalTransform.FromRotation(quaternion.RotateY(math.PI)) .TransformTransform( LocalTransform.FromPosition(new float3(0.0f, 0.0f, 1.0f)))); - World.GetOrCreateSystem().Update(World.Unmanaged); // Connect parent and child - World.GetOrCreateSystem().Update(World.Unmanaged); // Write to the child's LocalToWorld + World.GetOrCreateSystem().Update(World.Unmanaged); // Connect parent and child + World.GetOrCreateSystem().Update(World.Unmanaged); // Write to the child's LocalToWorld m_Manager.CompleteAllTrackedJobs(); var childWorldPosition = m_Manager.GetComponentData(child).Position; @@ -57,12 +55,12 @@ public void TRS_RemovedParentDoesNotAffectChildPosition() var child = m_Manager.CreateEntity(typeof(LocalToWorld), typeof(Parent), typeof(LocalTransform)); m_Manager.SetComponentData(parent, LocalTransform.Identity); - m_Manager.SetComponentData(child, new Parent {Value = parent}); + m_Manager.SetComponentData(child, new Parent { Value = parent }); m_Manager.SetComponentData(child, LocalTransform.FromPositionRotation(new float3(0.0f, 0.0f, 1.0f), quaternion.RotateY(math.PI))); - World.GetOrCreateSystem().Update(World.Unmanaged); // Connect parent and child - World.GetOrCreateSystem().Update(World.Unmanaged); // Write to the child's LocalToWorld + World.GetOrCreateSystem().Update(World.Unmanaged); // Connect parent and child + World.GetOrCreateSystem().Update(World.Unmanaged); // Write to the child's LocalToWorld m_Manager.CompleteAllTrackedJobs(); var expectedChildWorldPosition = m_Manager.GetComponentData(child).Position; @@ -72,8 +70,8 @@ public void TRS_RemovedParentDoesNotAffectChildPosition() m_Manager.SetComponentData(parent, LocalTransform.FromPositionRotation(new float3(0.0f, 0.0f, 1.0f), quaternion.RotateY((float)math.PI))); - World.GetOrCreateSystem().Update(World.Unmanaged); // Connect parent and child - World.GetOrCreateSystem().Update(World.Unmanaged); // Write to the child's LocalToWorld + World.GetOrCreateSystem().Update(World.Unmanaged); // Connect parent and child + World.GetOrCreateSystem().Update(World.Unmanaged); // Write to the child's LocalToWorld m_Manager.CompleteAllTrackedJobs(); var childWorldPosition = m_Manager.GetComponentData(child).Position; @@ -138,9 +136,9 @@ public TestHierarchy(World world, EntityManager manager) // 14: - R:[0] T:[2] // 15: - R:[0] T:[2] - rotationIndices = new int[] {0, 1, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 0, 0, 0, 0}; - translationIndices = new int[] {0, 1, 0, 1, 2, 0, 1, 2, 2, 0, 1, 2, 0, 1, 2, 2}; - parentIndices = new int[] {-1, 0, 1, 1, 1, 4, 4, 4, 0, 8, 8, 8, 11, 12, 13, 14}; + rotationIndices = new int[] { 0, 1, 2, 2, 2, 1, 1, 1, 2, 1, 1, 1, 0, 0, 0, 0 }; + translationIndices = new int[] { 0, 1, 0, 1, 2, 0, 1, 2, 2, 0, 1, 2, 0, 1, 2, 2 }; + parentIndices = new int[] { -1, 0, 1, 1, 1, 4, 4, 4, 0, 8, 8, 8, 11, 12, 13, 14 }; } public int Count => rotationIndices.Length; @@ -203,14 +201,14 @@ private void CreateInternal(EntityArchetype bodyArchetype, EntityArchetype rootA var translationIndex = translationIndices[i]; var transform = LocalTransform.FromPositionRotationScale( - translations[translationIndex], rotations[rotationIndex], scaleValue); + translations[translationIndex], rotations[rotationIndex], scaleValue); m_Manager.SetComponentData(bodyEntities[i], transform); } for (int i = 1; i < 16; i++) { var parentIndex = parentIndices[i]; - m_Manager.SetComponentData(bodyEntities[i], new Parent() {Value = bodyEntities[parentIndex]}); + m_Manager.SetComponentData(bodyEntities[i], new Parent() { Value = bodyEntities[parentIndex] }); } } @@ -235,7 +233,7 @@ public Entity CreateWithWriteGroupChildren() m_Manager.SetComponentData(root, LocalTransform.Identity); m_Manager.SetComponentData(child, LocalTransform.Identity); - m_Manager.SetComponentData(child, new TestWriteGroupComponent{Value = 42}); + m_Manager.SetComponentData(child, new TestWriteGroupComponent { Value = 42 }); m_Manager.SetComponentData(child, new Parent { Value = root }); m_Manager.SetComponentData(childChild, LocalTransform.Identity); @@ -246,8 +244,8 @@ public Entity CreateWithWriteGroupChildren() public void Update() { - World.GetOrCreateSystem().Update(World.Unmanaged); // Connect parent and child - World.GetOrCreateSystem().Update(World.Unmanaged); // Write to the child's LocalToWorld + World.GetOrCreateSystem().Update(World.Unmanaged); // Connect parent and child + World.GetOrCreateSystem().Update(World.Unmanaged); // Write to the child's LocalToWorld // Force complete so that main thread (tests) can have access to direct editing. m_Manager.CompleteAllTrackedJobs(); @@ -322,8 +320,8 @@ public void ChangeSomeParents() parentIndices[4] = 3; parentIndices[8] = 7; - m_Manager.SetComponentData(Entities[4], new Parent {Value = Entities[3]}); - m_Manager.SetComponentData(Entities[8], new Parent {Value = Entities[7]}); + m_Manager.SetComponentData(Entities[4], new Parent { Value = Entities[3] }); + m_Manager.SetComponentData(Entities[8], new Parent { Value = Entities[7] }); } public void DeleteSomeParents() @@ -448,7 +446,7 @@ public void TRS_TestHierarchyAddExtraChildren() for (int j = 0; j < childCount; j++) { var child = m_Manager.CreateEntity(typeof(LocalToWorld), typeof(Parent), typeof(LocalTransform)); - m_Manager.SetComponentData(child, new Parent {Value = parent}); + m_Manager.SetComponentData(child, new Parent { Value = parent }); m_Manager.SetComponentData(child, LocalTransform.Identity); } } @@ -475,7 +473,7 @@ public void TRS_TestHierarchyAddExtraChildrenChangeArchetype() for (int j = 0; j < childCount; j++) { var child = m_Manager.CreateEntity(typeof(LocalToWorld)); - m_Manager.AddComponentData(child, new Parent {Value = parent}); + m_Manager.AddComponentData(child, new Parent { Value = parent }); m_Manager.AddComponentData(child, LocalTransform.Identity); } } @@ -502,7 +500,7 @@ public void TRS_TestHierarchyAddExtraChildrenTwiceChangeArchetype() for (int j = 0; j < childCount; j++) { var child = m_Manager.CreateEntity(typeof(LocalToWorld)); - m_Manager.AddComponentData(child, new Parent {Value = parent}); + m_Manager.AddComponentData(child, new Parent { Value = parent }); m_Manager.AddComponentData(child, LocalTransform.Identity); } } @@ -516,7 +514,7 @@ public void TRS_TestHierarchyAddExtraChildrenTwiceChangeArchetype() for (int j = 0; j < childCount; j++) { var child = m_Manager.CreateEntity(typeof(LocalToWorld)); - m_Manager.AddComponentData(child, new Parent {Value = parent}); + m_Manager.AddComponentData(child, new Parent { Value = parent }); m_Manager.AddComponentData(child, LocalTransform.Identity); } } @@ -542,13 +540,13 @@ public void TRS_TestHierarchyAddExtraChildrenInnerChangeArchetype() for (int j = 0; j < childCount; j++) { var child = m_Manager.CreateEntity(typeof(LocalToWorld)); - m_Manager.AddComponentData(child, new Parent {Value = parent}); + m_Manager.AddComponentData(child, new Parent { Value = parent }); m_Manager.AddComponentData(child, LocalTransform.Identity); for (int k = 0; k < childCount; k++) { var nextChild = m_Manager.CreateEntity(typeof(LocalToWorld)); - m_Manager.AddComponentData(nextChild, new Parent {Value = child}); + m_Manager.AddComponentData(nextChild, new Parent { Value = child }); m_Manager.AddComponentData(nextChild, LocalTransform.Identity); } } @@ -577,7 +575,7 @@ public void TRS_TestHierarchyAddExtraChildrenChangeParent() for (int j = 0; j < childCount; j++) { var child = m_Manager.CreateEntity(typeof(LocalToWorld), typeof(Parent), typeof(LocalTransform)); - m_Manager.SetComponentData(child, new Parent {Value = parent}); + m_Manager.SetComponentData(child, new Parent { Value = parent }); m_Manager.AddComponentData(child, LocalTransform.Identity); children[(i * childCount) + j] = child; @@ -591,7 +589,7 @@ public void TRS_TestHierarchyAddExtraChildrenChangeParent() { var parent = testHierarchy.Entities[0]; var child = children[i]; - m_Manager.SetComponentData(child, new Parent {Value = parent}); + m_Manager.SetComponentData(child, new Parent { Value = parent }); } testHierarchy.Update(); @@ -616,7 +614,7 @@ public void TRS_TestHierarchyAddExtraChildrenChangeParentAgain() for (int j = 0; j < childCount; j++) { var child = m_Manager.CreateEntity(typeof(LocalToWorld), typeof(Parent), typeof(LocalTransform)); - m_Manager.SetComponentData(child, new Parent {Value = parent}); + m_Manager.SetComponentData(child, new Parent { Value = parent }); children[(i * childCount) + j] = child; } } @@ -630,7 +628,7 @@ public void TRS_TestHierarchyAddExtraChildrenChangeParentAgain() for (int j = 0; j < childCount; j++) { var child = children[(i * childCount) + j]; - m_Manager.SetComponentData(child, new Parent {Value = parent}); + m_Manager.SetComponentData(child, new Parent { Value = parent }); } } @@ -682,6 +680,7 @@ struct ScaledEntityHierarchy public LocalTransform ltA, ltB, ltC, ltD, ltE, ltF, ltG, ltH; public float4x4 ptmC, ptmD, ptmE, ptmF; } + void CreateScaledEntityHierarchy(out ScaledEntityHierarchy h) { // Create a hierarchy of entities. All have LocalTransform and LocalToWorld. In addition: @@ -711,15 +710,16 @@ void CreateScaledEntityHierarchy(out ScaledEntityHierarchy h) m_Manager.AddComponentData(h.entF, new Parent { Value = h.entC }); m_Manager.AddComponentData(h.entG, new Parent { Value = h.entF }); m_Manager.AddComponentData(h.entH, new Parent { Value = h.entF }); + // Initialize LocalTransforms - h.ltA = LocalTransform.FromPositionRotationScale(new float3(1,2,3), quaternion.RotateX(math.PI), 1.0f); - h.ltB = LocalTransform.FromPositionRotationScale(new float3(1,2,3), quaternion.RotateX(math.PI), 2.0f); - h.ltC = LocalTransform.FromPositionRotationScale(new float3(1,2,3), quaternion.RotateX(math.PI), 1.0f); - h.ltD = LocalTransform.FromPositionRotationScale(new float3(1,2,3), quaternion.RotateX(math.PI), 1.0f); - h.ltE = LocalTransform.FromPositionRotationScale(new float3(1,2,3), quaternion.RotateX(math.PI), 1.0f); - h.ltF = LocalTransform.FromPositionRotationScale(new float3(1,2,3), quaternion.RotateX(math.PI), 1.0f); - h.ltG = LocalTransform.FromPositionRotationScale(new float3(1,2,3), quaternion.RotateX(math.PI), 3.0f); - h.ltH = LocalTransform.FromPositionRotationScale(new float3(1,2,3), quaternion.RotateX(math.PI), 1.0f); + h.ltA = LocalTransform.FromPositionRotationScale(new float3(1, 2, 3), quaternion.RotateX(math.PI), 1.0f); + h.ltB = LocalTransform.FromPositionRotationScale(new float3(1, 2, 3), quaternion.RotateX(math.PI), 2.0f); + h.ltC = LocalTransform.FromPositionRotationScale(new float3(1, 2, 3), quaternion.RotateX(math.PI), 1.0f); + h.ltD = LocalTransform.FromPositionRotationScale(new float3(1, 2, 3), quaternion.RotateX(math.PI), 1.0f); + h.ltE = LocalTransform.FromPositionRotationScale(new float3(1, 2, 3), quaternion.RotateX(math.PI), 1.0f); + h.ltF = LocalTransform.FromPositionRotationScale(new float3(1, 2, 3), quaternion.RotateX(math.PI), 1.0f); + h.ltG = LocalTransform.FromPositionRotationScale(new float3(1, 2, 3), quaternion.RotateX(math.PI), 3.0f); + h.ltH = LocalTransform.FromPositionRotationScale(new float3(1, 2, 3), quaternion.RotateX(math.PI), 1.0f); m_Manager.SetComponentData(h.entA, h.ltA); m_Manager.SetComponentData(h.entB, h.ltB); m_Manager.SetComponentData(h.entC, h.ltC); @@ -728,20 +728,21 @@ void CreateScaledEntityHierarchy(out ScaledEntityHierarchy h) m_Manager.SetComponentData(h.entF, h.ltF); m_Manager.SetComponentData(h.entG, h.ltG); m_Manager.SetComponentData(h.entH, h.ltH); + // Add PostTransformMatrix where applicable var ptmTypes = new ComponentTypeSet(typeof(PostTransformMatrix)); m_Manager.AddComponent(h.entC, ptmTypes); m_Manager.AddComponent(h.entD, ptmTypes); m_Manager.AddComponent(h.entE, ptmTypes); m_Manager.AddComponent(h.entF, ptmTypes); - h.ptmC = float4x4.Scale(1,2,3); - h.ptmD = float4x4.Scale(4,5,6); - h.ptmE = float4x4.Scale(7,8,9); - h.ptmF = float4x4.Scale(10,11,12); - m_Manager.SetComponentData(h.entC, new PostTransformMatrix{Value = h.ptmC}); - m_Manager.SetComponentData(h.entD, new PostTransformMatrix{Value = h.ptmD}); - m_Manager.SetComponentData(h.entE, new PostTransformMatrix{Value = h.ptmE}); - m_Manager.SetComponentData(h.entF, new PostTransformMatrix{Value = h.ptmF}); + h.ptmC = float4x4.Scale(1, 2, 3); + h.ptmD = float4x4.Scale(4, 5, 6); + h.ptmE = float4x4.Scale(7, 8, 9); + h.ptmF = float4x4.Scale(10, 11, 12); + m_Manager.SetComponentData(h.entC, new PostTransformMatrix { Value = h.ptmC }); + m_Manager.SetComponentData(h.entD, new PostTransformMatrix { Value = h.ptmD }); + m_Manager.SetComponentData(h.entE, new PostTransformMatrix { Value = h.ptmE }); + m_Manager.SetComponentData(h.entF, new PostTransformMatrix { Value = h.ptmF }); } void ValidateScaledEntityHierarchy(in ScaledEntityHierarchy h) @@ -775,8 +776,10 @@ public void TransformHierarchy_ComplexScale_Works() transformSystemGroup.AddSystemToUpdateList(parentSystem); transformSystemGroup.AddSystemToUpdateList(localToWorldSystem); transformSystemGroup.SortSystems(); + // Create the hierarchy CreateScaledEntityHierarchy(out ScaledEntityHierarchy h); + // Tick & validate transformSystemGroup.Update(); ValidateScaledEntityHierarchy(h); @@ -791,9 +794,9 @@ Entity SimpleHiearchy() var child = m_Manager.CreateEntity(typeof(LocalToWorld), typeof(Parent), typeof(LocalTransform)); var linkedEntityGroup = m_Manager.GetBuffer(parent); - linkedEntityGroup.Add(new LinkedEntityGroup {Value = parent}); - linkedEntityGroup.Add(new LinkedEntityGroup {Value = child}); - m_Manager.SetComponentData(child, new Parent {Value = parent}); + linkedEntityGroup.Add(new LinkedEntityGroup { Value = parent }); + linkedEntityGroup.Add(new LinkedEntityGroup { Value = child }); + m_Manager.SetComponentData(child, new Parent { Value = parent }); return parent; } @@ -831,9 +834,9 @@ Entity SimpleHiearchy() var child = m_Manager.CreateEntity(typeof(LocalToWorld), typeof(Parent), typeof(LocalTransform)); var linkedEntityGroup = m_Manager.GetBuffer(parent); - linkedEntityGroup.Add(new LinkedEntityGroup {Value = parent}); - linkedEntityGroup.Add(new LinkedEntityGroup {Value = child}); - m_Manager.SetComponentData(child, new Parent {Value = parent}); + linkedEntityGroup.Add(new LinkedEntityGroup { Value = parent }); + linkedEntityGroup.Add(new LinkedEntityGroup { Value = child }); + m_Manager.SetComponentData(child, new Parent { Value = parent }); return parent; } @@ -888,12 +891,12 @@ public void TRS_SetParent_OldParentIsDestroyed([Values] bool withCleanup) m_Manager.AddComponent(parentA); } - m_Manager.SetComponentData(child, new Parent {Value = parentA}); + m_Manager.SetComponentData(child, new Parent { Value = parentA }); parentSystem.Update(World.Unmanaged); m_Manager.DestroyEntity(parentA); - m_Manager.SetComponentData(child, new Parent {Value = parentB}); + m_Manager.SetComponentData(child, new Parent { Value = parentB }); parentSystem.Update(World.Unmanaged); @@ -959,6 +962,7 @@ public void TRS_ChangeParent_SetsExpectedComponents() // Make sure old parent has expected components. Assert.IsFalse(m_Manager.HasComponent(parentA)); + // Make sure new parent has expected components Assert.IsTrue(m_Manager.HasComponent(parentB)); var childrenB = m_Manager.GetBuffer(parentB); @@ -1023,7 +1027,7 @@ public void TRS_LocalToWorldRotationWithNonuniformScale() { var entity = m_Manager.CreateEntity(typeof(LocalToWorld), typeof(LocalTransform), typeof(PostTransformMatrix)); m_Manager.SetComponentData(entity, LocalTransform.FromRotation(quaternion.Euler(math.radians(new float3(10f, 20f, 30f))))); - m_Manager.SetComponentData(entity, new PostTransformMatrix {Value = float4x4.Scale(1f, 2f, 3f)}); + m_Manager.SetComponentData(entity, new PostTransformMatrix { Value = float4x4.Scale(1f, 2f, 3f) }); World.GetOrCreateSystem().Update(World.Unmanaged); var rotationFromTransform = m_Manager.GetComponentData(entity).Rotation; var rotationFromMatrix = m_Manager.GetComponentData(entity).Rotation; diff --git a/Unity.Transforms/LocalTransform.cs b/Unity.Transforms/LocalTransform.cs index a38e096..633926e 100644 --- a/Unity.Transforms/LocalTransform.cs +++ b/Unity.Transforms/LocalTransform.cs @@ -11,7 +11,7 @@ namespace Unity.Transforms { /// - /// Position, rotation and scale of this entity, relative to the parent, or on world space, if no parent exists. + /// Position, rotation and scale of this entity, relative to the parent, or in world space, if no parent exists. /// /// /// If the entity has a component, LocalTransform is relative to that parent. @@ -89,35 +89,43 @@ public static LocalTransform FromMatrixSafe(float4x4 matrix) var tolerancesq = tolerance * tolerance; // Test for uniform scale - var scaleX = math.lengthsq(matrix.c0.xyz); - var scaleY = math.lengthsq(matrix.c1.xyz); - var scaleZ = math.lengthsq(matrix.c2.xyz); + var scaleXsq = math.lengthsq(matrix.c0.xyz); + var scaleYsq = math.lengthsq(matrix.c1.xyz); + var scaleZsq = math.lengthsq(matrix.c2.xyz); - if (math.abs(scaleX - scaleY) > tolerancesq || math.abs(scaleX - scaleZ) > tolerancesq) + if (math.abs(scaleXsq - scaleYsq) > tolerancesq || math.abs(scaleXsq - scaleZsq) > tolerancesq) { throw new ArgumentException("Trying to convert a float4x4 to a LocalTransform, but the scale is not uniform"); } + var scaleX = math.sqrt(scaleXsq); + var scaleY = math.sqrt(scaleYsq); + var scaleZ = math.sqrt(scaleZsq); + + var scale = math.max(scaleX, math.max(scaleY, scaleZ)); + var matrix3x3 = new float3x3(matrix); - var transpose3x3 = math.transpose(matrix3x3); - var combined3x3 = math.mul(matrix3x3, transpose3x3); + + float dot01 = math.dot(matrix3x3.c0, matrix3x3.c1); + float dot02 = math.dot(matrix3x3.c0, matrix3x3.c2); + float dot12 = math.dot(matrix3x3.c1, matrix3x3.c2); // If the matrix is orthogonal, the combined result should be identity - if (math.lengthsq(combined3x3.c0 - math.right()) > tolerancesq || - math.lengthsq(combined3x3.c1 - math.up()) > tolerancesq || - math.lengthsq(combined3x3.c2 - math.right()) > tolerancesq) + if ( math.abs(dot01) > tolerancesq || + math.abs(dot02) > tolerancesq || + math.abs(dot12) > tolerancesq) { throw new ArgumentException("Trying to convert a float4x4 to a LocalTransform, but the rotation 3x3 is not orthogonal"); } - float3x3 normalizedRotationMatrix = math.orthonormalize(new float3x3(matrix)); + float3x3 normalizedRotationMatrix = math.orthonormalize(matrix3x3); var rotation = new quaternion(normalizedRotationMatrix); var position = matrix.c3.xyz; var transform = default(LocalTransform); transform.Position = position; - transform.Scale = scaleX; + transform.Scale = scale; transform.Rotation = rotation; return transform; } diff --git a/Unity.Transforms/ParentSystem.cs b/Unity.Transforms/ParentSystem.cs index bf0003e..79e0923 100644 --- a/Unity.Transforms/ParentSystem.cs +++ b/Unity.Transforms/ParentSystem.cs @@ -435,38 +435,21 @@ public void OnUpdate(ref SystemState state) { state.Dependency.Complete(); - // TODO: these dotsruntime ifdefs are a workaround for a crash - BUR-1767 -#if !UNITY_DOTSRUNTIME k_ProfileDeletedParents.Begin(); -#endif UpdateDeletedParents(ref state); -#if !UNITY_DOTSRUNTIME k_ProfileDeletedParents.End(); -#endif -#if !UNITY_DOTSRUNTIME k_ProfileRemoveParents.Begin(); -#endif UpdateRemoveParents(ref state); -#if !UNITY_DOTSRUNTIME k_ProfileRemoveParents.End(); -#endif -#if !UNITY_DOTSRUNTIME k_ProfileNewParents.Begin(); -#endif UpdateNewParents(ref state); -#if !UNITY_DOTSRUNTIME k_ProfileNewParents.End(); -#endif -#if !UNITY_DOTSRUNTIME k_ProfileChangeParents.Begin(); -#endif UpdateChangeParents(ref state); -#if !UNITY_DOTSRUNTIME k_ProfileChangeParents.End(); -#endif } } } diff --git a/Unity.Transforms/TransformHelpers.cs b/Unity.Transforms/TransformHelpers.cs index 2107bb0..080fac0 100644 --- a/Unity.Transforms/TransformHelpers.cs +++ b/Unity.Transforms/TransformHelpers.cs @@ -116,6 +116,9 @@ public static float3 Scale(in this float4x4 m) => // ------------------ Coordinate system conversion /// Transforms a 3D point by a 4x4 transformation matrix. + /// + /// This method assumes that is an affine transformation matrix. + /// /// A transformation matrix /// A 3D position /// @@ -124,6 +127,9 @@ public static float3 Scale(in this float4x4 m) => public static float3 TransformPoint(in this float4x4 m, in float3 p) => math.mul(m, new float4(p, 1)).xyz; /// Transforms a 3D direction by a 4x4 transformation matrix. + /// + /// This method assumes that is an affine transformation matrix. + /// /// A transformation matrix /// A vector representing a direction in 3D space. This vector does not need to be normalized. /// @@ -132,6 +138,9 @@ public static float3 Scale(in this float4x4 m) => public static float3 TransformDirection(in this float4x4 m, in float3 d) => math.rotate(m, d); /// Transforms a 3D rotation by a 4x4 transformation matrix. + /// + /// This method assumes that is an affine transformation matrix without shear. + /// /// A transformation matrix /// A quaternion representing a 3D rotation. This quaternion does not need to be normalized. /// @@ -141,6 +150,9 @@ public static quaternion TransformRotation(in this float4x4 m, in quaternion q) math.mul(new quaternion(math.orthonormalize(new float3x3(m))), q); /// Transforms a 3D point by the inverse of a 4x4 transformation matrix. + /// + /// This method assumes that is an affine transformation matrix. + /// /// A transformation matrix /// A 3D position /// @@ -149,6 +161,9 @@ public static quaternion TransformRotation(in this float4x4 m, in quaternion q) public static float3 InverseTransformPoint(in this float4x4 m, in float3 p) => math.mul(math.inverse(m), new float4(p, 1)).xyz; /// Transforms a 3D direction by the inverse of a 4x4 transformation matrix. + /// + /// This method assumes that is an affine transformation matrix. + /// /// A transformation matrix /// A vector representing a direction in 3D space. This vector does not need to be normalized. /// @@ -157,6 +172,9 @@ public static quaternion TransformRotation(in this float4x4 m, in quaternion q) public static float3 InverseTransformDirection(in this float4x4 m, in float3 d) => math.rotate(math.inverse(m), d); /// Transforms a 3D rotation by the inverse of a 4x4 transformation matrix. + /// + /// This method assumes that is an affine transformation matrix without shear. + /// /// A transformation matrix /// A quaternion representing a 3D rotation. This quaternion does not need to be normalized. /// diff --git a/ValidationExceptions.json b/ValidationExceptions.json deleted file mode 100644 index 833e5cb..0000000 --- a/ValidationExceptions.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "ErrorExceptions": [ - { - "ValidationTest": "Package Unity Version Validation", - "ExceptionMessage": "The Unity version requirement is more strict than in the previous version of the package. Increment the minor version of the package to leave patch versions available for previous version. Read more about this error and potential solutions at https://docs.unity3d.com/Packages/com.unity.package-validation-suite@latest/index.html?preview=1&subfolder=/manual/package_unity_version_validation_error.html#the-unity-version-requirement-is-more-strict-than-in-the-previous-version-of-the-package", - "PackageVersion": "1.0.14" - } - ], - "WarningExceptions": [] -} diff --git a/ValidationExceptions.json.meta b/ValidationExceptions.json.meta deleted file mode 100644 index 91e69ab..0000000 --- a/ValidationExceptions.json.meta +++ /dev/null @@ -1,7 +0,0 @@ -fileFormatVersion: 2 -guid: 4c57063f4b09b3743ac9e7d38c74fdcd -TextScriptImporter: - externalObjects: {} - userData: - assetBundleName: - assetBundleVariant: diff --git a/package.json b/package.json index 6c9f80a..2e6ceda 100644 --- a/package.json +++ b/package.json @@ -1,13 +1,13 @@ { "name": "com.unity.entities", "displayName": "Entities", - "version": "1.0.14", + "version": "1.1.0-exp.1", "unity": "2022.3", "unityRelease": "0f1", "dependencies": { - "com.unity.burst": "1.8.7", + "com.unity.burst": "1.8.8", "com.unity.serialization": "3.1.1", - "com.unity.collections": "2.1.4", + "com.unity.collections": "2.3.0-exp.1", "com.unity.mathematics": "1.2.6", "com.unity.modules.assetbundle": "1.0.0", "com.unity.modules.audio": "1.0.0", @@ -24,15 +24,15 @@ "unity" ], "_upm": { - "changelog": "### Added\n\n* Obsolete API containing `evt.PreventDefault()` for `evt.StopPropagation()` in IListElement.cs.\n* Added a dependency on the com.unity.test-framework.performance package\n* Clean up for ComponentSystemGroup\n* Early null entity check in ECB commands\n* More error logging in DotsGlobalSettings\n* Error logging in UpdateLoadOperation()\n* Added `EntityCommandBuffer.MoveComponent(Entity src, Entity dst)`\n\n### Changed\n\n* Updated Burst dependency to version 1.8.7\n* Replaced an erroneous NUnit.Framework dependency in EntitySceneBuildUtility from an Assert.Equal() call with UnityEngine.Assertions\n* Obsolete API containing `FindObjectsOfType<>()` for `FindObjectsByType<>(FindObjectsSortMode.None)` in SubSceneInspectorUtility.cs and `FindObjectOfType<>()` for `FindFirstObjectByType<>()` in LiveConversionEditorPerformanceTests.cs and LiveConversionEditorTests.cs.\n* Increased allocation size in RuntimeContentManager initialization\n* More informative error message in WriteSceneHeader()\n\n\n### Removed\n\n* Alignment attribute is removed when displaying component attributes in Inspector window.\n\n### Fixed\n\n* Avoid unnecessary sync point in `EntityCommandBufferSystem.OnUpdate()` if no command buffers were recorded.\n* Adding and removing shared components and tag components to an entire chunk now correctly updates the per-component order versions. This most commonly manifested as errors in the element order of the data returned by `EntityQuery.GetTransformAccessArray()`.\n* Use default inspector when an asset is selected regardless of the data mode.\n* Assertion failure when undoing entity selection is fixed.\n* Not being able to access Entity item from tuple when using `SystemAPI.Query` and `WithEntityAccess`.\n* `BurstCompatibleAspect` is now `readonly`, as required by `IAspect` implementations.\n* VisualElement null exception upon entities tooling windows' opening is fixed.\n* Using `EnabledRefRW` and `EnabledMask` to set enableable component state to its existing value (e.g. disabling a component that's already disabled) no longer causes an \"internal consistency check\" failure when the EntityManager is disposed.\n* Fixed potential uninitialized memory access when creating an `EntityQuery` with multiple `EntityQueryDesc` elements.\n* Components tab update will check the target's existing state first.\n* Check windows' initialization state before any actions in `OnDisable()`.\n* Crash in EntityManager.SetArchetype() when the new archetype is the same as the current one.\n* crash in BlobAssetReference\n* Crash in BakingSystemFilterSettings\n* Clarified documentation for the unusual semantics of `EntityManager.SetComponentEnabled(EntityQuery)`, which ignores the current status of enableable components and processes all entities in all of the query's matching chunks.\n* Better error messages when `EntityManager`'s internal consistency checks fail at shutdown.\n* `EntityManager.MoveComponent(Entity src, Entity dst)` now throws if `dst` already has the component `T`. This case has never been supported; previously, the existing value would be quietly leaked.\n* Fixed stack overflow that was the result of circular dependency data in the content." + "changelog": "### Added\n\n* Added a \"Custom Transform System\" folder in the assets folder of the \"EntitiesSamples\" project.\n* missing documentation on search public api\n* Enabled model assets to be baked as prefabs using EntityPrefabReference.\n* Error DC0084 is generated when capturing a local variable in an Entities.ForEach that isn't used.\n* Add GetSharedComponentIndexManaged API\n* Added addition errors around improper use of SystemAPI methods with generic type arguments.\n* Search Keyword registration for entities preferences and settings.\n* `SystemAPI.Query` now supports `WithSharedComponentFilterManaged(T sharedComponent)` and `WithSharedComponentFilterManaged(T1 sharedComponent1, T2 sharedComponent2)`.\n* `CompleteDependencyBeforeRW(SystemState state)` and `CompleteDependencyBeforeRO(SystemState state)` are added to the public `Unity.Entities.IAspectCreate` interface in order to faciltiate changes to source-generated code. Implementations of these methods, like all existing methods in `Unity.Entities.IAspectCreate`, will automatically be generated on users' behalf by source generators.\n* Added `IBaker.CreateAdditionalEntities` for creating multiple additional entities at once.\n* filter to search with SharedComponent from within the Hierarchy Window\n* filter to search with SharedComponent from within the Search Window\n* New `EntityQuery` component type constraint: `Present` components are required to be present on a query's matching archetypes, whether or not they are enabled or disabled on individual entities. This constraint can be added in all the usual places -- `EntityQueryBuilder.WithPresent()`, `EntityQueryDesc.Present[]`, the `[WithPresent(typeof(T))]` attribute on `IJobEntity`, etc.\n* checks to see if an exclusive transaction is active while scheduling a job.\n* Specific error when capturing a variable in `Entities.ForEach` that relies on relies on source generators (since there is no deterministic order between source generators, this can be an error).\n\n### Changed\n\n* Significantly improved the performance of `EntityManager.SetSharedComponent(EntityQuery,T)` and `EntityManager.SetSharedComponentManaged(EntityQuery,T)`\n* `TypeManager.Initialize` will disable synchronous Burst compilation only during initialization of the TypeManager so that large synchronous compilation stalls when compiling function pointers can be avoided when iterating in the Editor.\n* BlobBuilder is now partial\n* CompanionGameObjectUpdateTransformSystem is now public\n* ResetUpdateAllocator is now public\n* SubSceneInspectorUtility is now public\n* Batch primary entity creation in baking.\n* `HasSingleton()` and `TryGetSingleton()` methods will now throw if they find >1 instance of `T`, instead of returning `false`. Having more than one instance of a singleton component indicates a bug in the program, and should fail more obviously.\n* The visibility of the `EnabledBitUtility` class was changed from `public` to `internal`. This class was never intended to be part of the public API of the Entities package, and should not be used by application code.\n* `EntityManager.AddChunkComponentData(EntityQuery,T)` no longer throws an exception if the component `T` is already present on any of the target chunks. Instead, the new value is assigned to the existing component. This matches the behavior of other AddComponent variants in the Entities package.\n* Text for exception that occurs when an entity doesn't exist during EntityCommandBuffer playback.\n* `IJobChunk` now allows indexed writes to native containers passed in the job struct. Only writes to the element at `unfilteredChunkIndex` are valid. To disable this check on a per-container basis, add `[NativeDisableParallelForRestriction]` to the relevant field in the job struct.\n\n### Deprecated\n\n* Deprecate GetSharedComponentDataIndex as it is a dupplicate of GetSharedComponentIndex\n\n### Removed\n\n* Removed gizmo rendering logic for entities from C#, now this is handled natively in Unity.\n* Marked `EntityManager.Instantiate(NativeArray srcEntities, NativeArray outputEntities)` as obsolete, with the intention to eventually remove it entirely.\n\n### Fixed\n\n* Code fix now available to rewrite offending code that trigger `CS1654` errors.\n* Fixed a typo in the \"LocalTransform\" summary comment.\n* Property drawer for arrays and lists of Weak(Object|Scene)Reference types\n* Compilation with DISABLE_ENTITIES_JOURNALING works again.\n* Ensure Component gizmos are rendered for GameObjects in SubScenes when rendered with Entities. That is when using Preferences->Entities->SceneView Mode -> Runtime Data.\n* Added null checks in BlobAssetReferenceData properties to avoid crashing the engine while inspecting variables with the debugger.\n* LocalTransform.FromMatrixSafe would throw exceptions for valid matricies\n* IJobEntity overwriting files due to colliding filenames.\n* Differ discards ChunkComponents added during baking.\n* Fixed issue with BakingVersion(true) triggering warnings in the console log about the attribute being missing on the type you placed this on in cases where named BakingVersion attributes are being used in the containing assembly.\n* A source generator error is not thrown anymore when using the fully qualified name of `SystemAPI.Time` (e.g. `Unity.Entities.SystemAPI.Time`).\n* The main entity in LinkedEntityGroups were not added in incremental baking.\n* Fix memory leak in the BakerEntityUsage not disposing properly the list of ReferencedEntityUsage\n* Fixed `isReadOnly` being ignored in `EntityManager.GetBuffer`.\n* ArgumentException on an unknown type when using the GetComponent API in a baker with an abstract type.\n* Subscenes no longer redundantly rebake on recompile due to type order changes.\n* Serialization of blob asset references in unmanaged shared components\n* `SystemGenerator` in the source-generation solution runs in ~48% less time when tested on a small game project shared by one of our users.\n* You now no longer get a compile error for methods containing SystemAPI, EFE, or IJE scheduling, that include a signature with nullables, multiple generics, or parameter modifiers.\n* KeyNotFoundException thrown by the Entities.Editor.HierarchyWindow when loading a new gameobject scene\n* Users can now specify duplicate components in the same `IJobEntity.Execute()` method, insofar as exactly one of them is wrapped in `EnabledRef`.\n* Fixed issue where ambiguous types used in a `SystemAPI.Query()` call would generate a compiler error from source generators.\n* Entities Hierarchy: when entering playmode without fast enter playmode the hierarchy was showing the authoring datamode content even though the switch in the window header was showing the mixed datamode.\n* Validation for world's existence before accessing within EntityContainer and EntitySelectionProxy." }, "upmCi": { - "footprint": "741f95a20c59c88b1b0dcaeac7ab5251c037dde6" + "footprint": "685539d286ea7375559f1bddcc9805d9885bb432" }, - "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.entities@1.0/manual/index.html", + "documentationUrl": "https://docs.unity3d.com/Packages/com.unity.entities@1.1/manual/index.html", "repository": { "url": "https://github.cds.internal.unity3d.com/unity/dots.git", "type": "git", - "revision": "13433ff314ae6503bb855899b83d26a063650441" + "revision": "c3274ed2015eeec85914d62bfbd32e39f0dd112b" } }