Search Unity

Some issues when not using InjectionGroups

Discussion in 'Entity Component System' started by Silve, Dec 18, 2018.

  1. Silve

    Silve

    Joined:
    May 7, 2013
    Posts:
    5
    So I've been trying to learn how to go from InjectionGroups (since they are going to be deprecated) to the recommended way of working with ECS (ComponentGroups) but I've encountered several issues and I'd like some input on what I'm doing wrong and I'd like to voice some problems I have with this way of working.

    I'm going to outline my/our issues here but for a clearer example of what I mean please look at the code I've included.

    So what I'm trying to do with this script is modify a heatmap based on thing being placed in the world with the SharedComponent HeatmapModefier.
    My biggest issue right now is that it seems that the automatic input dependency simply does not work if other scripts are using InjectionGroups, this is a huge issue at the moment since it will be very hard converting all the code in one go. Our current solution is using JobHandle.Complete() everywhere until the conversion of all systems are done.

    Another concern I'm having is that this way of writing code is tedious since we first have to declare the ComponentGroup with all the needed components, than again define all the types that are going to be used and then finally use those types to get the actual values, it feels like that is one step too much.
    And getting shared component data into a job without stalling the main thread is even worse.

    I could not find a way to simply include a SharedComponent in a IJobProcessComponentData which I feel would make thing much simpler if possible, I understand that due to how SharedComponents work with chunks it might not be possible but then there should be an easier way to access the data.

    So if anyone has some good advice or insight that would be very valuable.

    Code (CSharp):
    1. public class HeatmapSystem : JobComponentSystem
    2. {
    3.     // Not sure if Inject for barriers is going to be deprecated but I haven't seen
    4.     // any other methods of getting the barrier so.
    5.     [Inject] EndFrameBarrier m_barrier;
    6.  
    7.     private ComponentGroup m_addedGroup;
    8.     private ComponentGroup m_removedGroup;
    9.  
    10.     private bool m_isMapUpdated;                    // Has the heatmap changed, i.e. should the texture map be updated
    11.     private NativeArray<float> m_heatmapValues;     // The actual values
    12.     private NativeList<Color32> m_heatmapColours;   // The clamped values translated into colour
    13.     private NativeArray<int4> m_textureBounds;      // The bounds of the heatmap that has to be updated
    14.     private Material m_material;                    // The material to apply the heatmap texture too
    15.     private Texture2D m_texture;                    // The heatmap texture
    16.  
    17.     private List<System.IDisposable> m_toDispose;   // Collection of all the NativeArrays/Lists that has to be disposed next frame
    18.  
    19.     protected override void OnCreateManager()
    20.     {
    21.         ...
    22.         ...
    23.        
    24.         m_addedGroup = GetComponentGroup(new EntityArchetypeQuery
    25.         {
    26.             None = new ComponentType[] { ComponentType.ReadOnly<HeatmapSyncComponent>() },
    27.             Any = new ComponentType[0],
    28.             All = new ComponentType[]
    29.             {
    30.                 ComponentType.ReadOnly<HeatmapModefier>(),
    31.                 ComponentType.ReadOnly<CellSize>(),
    32.                 ComponentType.ReadOnly<Position>(),
    33.                 ComponentType.ReadOnly<Rotation>()
    34.             }
    35.         });
    36.         m_removedGroup = GetComponentGroup(new EntityArchetypeQuery
    37.         {
    38.             None = new ComponentType[0],
    39.             Any = new ComponentType[0],
    40.             All = new ComponentType[]
    41.             {
    42.                 ComponentType.ReadOnly<MarkedForDestruction>(),
    43.                 ComponentType.ReadOnly<HeatmapModefier>(),
    44.                 ComponentType.ReadOnly<CellSize>(),
    45.                 ComponentType.ReadOnly<Position>(),
    46.                 ComponentType.ReadOnly<Rotation>()
    47.             }
    48.         });
    49.     }
    50.  
    51.     protected override JobHandle OnUpdate(JobHandle inputDeps)
    52.     {
    53.         ...
    54.         ...
    55.        
    56.         for (int i = 0; i < m_toDispose.Count; i++)
    57.         {
    58.             m_toDispose[i].Dispose();
    59.         }
    60.         m_toDispose.Clear();
    61.  
    62.         NativeArray<ArchetypeChunk> addChunks = m_addedGroup.CreateArchetypeChunkArray(Allocator.TempJob, out JobHandle handle);
    63.         m_toDispose.Add(addChunks);
    64.         inputDeps = JobHandle.CombineDependencies(inputDeps, handle);
    65.  
    66.         if (addChunks.Length > 0)
    67.             inputDeps = AddedModefier(ref addChunks, inputDeps);
    68.        
    69.         ...
    70.         ...
    71.  
    72.         if(m_isMapUpdated) // If a job has been sceduled this frame then..
    73.         {
    74.             // I need to call Complete() here otherwise other systems not yet updated
    75.             // i.e using InjectionGroups won't get the inputDependencies right and break,
    76.             // I want to know if I'm doing someting wrong or if this is a bug that is going to get fixed?
    77.             inputDeps.Complete();
    78.             m_heatmapColours = new NativeList<Color32>(Allocator.TempJob);
    79.             inputDeps = new UpdateHeatmapColourJob
    80.             {
    81.                 startColour = m_sysData.landValueGradient.Evaluate(0),
    82.                 endColour = m_sysData.landValueGradient.Evaluate(1),
    83.                 heatmapValues = m_heatmapValues,
    84.                 heatmapColours = m_heatmapColours,
    85.                 boundsArray = m_textureBounds
    86.             }.Schedule(inputDeps);
    87.         }
    88.  
    89.         return inputDeps;
    90.     }
    91.  
    92.     private JobHandle UpdateHeatmap(ref NativeArray<ArchetypeChunk> chunks, JobHandle inputDeps, bool invertValues = false)
    93.     {
    94.         ...
    95.         ...
    96.  
    97.         // I have to get two lists of my SharedComponent which I then have to convert
    98.         // to NativeArrays so that they may be used in a job.
    99.         // I find it utterly perplexing that they aren't NativeArrays from the start or a NativeHashMap with
    100.         // their global index as the key.
    101.         // Basically it should be possible to get this down to one or two lines of code.
    102.         List<CellSize> cellSizeLst = new List<CellSize>();
    103.         List<int> cellSizeLstIndices = new List<int>();
    104.         EntityManager.GetAllUniqueSharedComponentData(cellSizeLst, cellSizeLstIndices);
    105.         NativeArray<CellSize> cellSizeNA = new NativeArray<CellSize>(cellSizeLst.ToArray(), Allocator.TempJob);
    106.         NativeArray<int> cellSizeIndicesNA = new NativeArray<int>(cellSizeLstIndices.ToArray(), Allocator.TempJob);
    107.         m_toDispose.Add(cellSizeNA);
    108.         m_toDispose.Add(cellSizeIndicesNA);
    109.        
    110.         // Same as previous
    111.         List<HeatmapModefier> heatValueModLst = new List<HeatmapModefier>();
    112.         List<int> heatValueModListIndices = new List<int>();
    113.         EntityManager.GetAllUniqueSharedComponentData(heatValueModLst, heatValueModListIndices);
    114.         NativeArray<HeatmapModefier> heatValueModNA = new NativeArray<HeatmapModefier>(heatValueModLst.ToArray(), Allocator.TempJob);
    115.         NativeArray<int> heatValueModIndicesNA = new NativeArray<int>(heatValueModListIndices.ToArray(), Allocator.TempJob);
    116.         m_toDispose.Add(heatValueModNA);
    117.         m_toDispose.Add(heatValueModIndicesNA);
    118.  
    119.         for (int cI = 0; cI < chunks.Length; cI++)
    120.         {
    121.             inputDeps = new UpdateHeatmapValuesJob
    122.             {
    123.                 // What is the point of ArchetypeChunkComponentTypes?
    124.                 // Why can't I simply use the type directly with a paramater for R/W when getting the component NativeArray?
    125.                 entityType = EntityManager.GetArchetypeChunkEntityType(),
    126.                 positionType = EntityManager.GetArchetypeChunkComponentType<Position>(true),
    127.                 rotationType = EntityManager.GetArchetypeChunkComponentType<Rotation>(true),
    128.                 cellSizeType = EntityManager.GetArchetypeChunkSharedComponentType<CellSize>(),
    129.                 valueType = EntityManager.GetArchetypeChunkSharedComponentType<HeatmapModefier>(),
    130.                 cellSizeLst = cellSizeNA,
    131.                 cellSizeLstIndices = cellSizeIndicesNA,
    132.                 heatValueModLst = heatValueModNA,
    133.                 heatValueModListIndecies = heatValueModIndicesNA,
    134.                 chunks = chunks,
    135.                 chunkIndex = cI,
    136.                 heatmap = m_heatmapValues,
    137.                 boundsArray = m_textureBounds
    138.             }.Schedule(inputDeps);
    139.         }
    140.         return inputDeps;
    141.     }
    142.  
    143.     private JobHandle AddedModefier(ref NativeArray<ArchetypeChunk> chunks, JobHandle inputDeps)
    144.     {
    145.         inputDeps = UpdateHeatmap(ref chunks, inputDeps);
    146.         return new AddComponentToChunkJob<HeatmapSyncComponent>
    147.         {
    148.             buffer = m_barrier.CreateCommandBuffer().ToConcurrent(),
    149.             entityType = EntityManager.GetArchetypeChunkEntityType()
    150.         }.Schedule(m_addedGroup, inputDeps);
    151.     }
    152. }