Search Unity

Is there a way to debug what system modifies the component?

Discussion in 'Entity Component System' started by xVergilx, Jul 30, 2019.

  1. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    I've got a issue with systems being ran when filter is being applied like: SetFilterChanged(typeof(Translation)), and in theory it should not be modified prior to that system update. But it still runs like its marked with always update.

    Is there a way to debug whats marking that entity as translation changed?
     
  2. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    This is really puzzling, so here's more details:
    A system that I want to run only after Translation is modified:

    Code (CSharp):
    1. using SystemGroups;
    2. using Archetypes;
    3. using Components.Bounds;
    4. using Unity.Burst;
    5. using Unity.Entities;
    6. using Unity.Jobs;
    7. using Unity.Mathematics;
    8. using Unity.Transforms;
    9.  
    10. namespace Systems.Restrictions {
    11.     [UpdateInGroup(typeof(AfterTransformGroup))]
    12.     public class ClampToLevelBoundsSystem : JobComponentSystem {
    13.         #region [Fields]
    14.  
    15.         private EntityQuery _clampQuery;
    16.         private EntityQuery _boundsQuery;
    17.  
    18.         #endregion
    19.  
    20.         protected override void OnCreate() {
    21.             _clampQuery = GetEntityQuery(new EntityQueryDesc {
    22.                                                                  All = ArchetypesGeneric.TranslationReadWrite, //
    23. ComponentType.ReadWrite<Translation>()
    24.                                                                  Any = ArchetypesLogic.AnyMovement, //
    25. ComponentType.ReadOnly<MoveByDirection>()
    26.                                                                  None = ArchetypesGeneric.LocalToParentReadOnly, //
    27. ComponentType.ReadOnly<LocalToParent>()
    28.                                                              });
    29.             _clampQuery.SetFilterChanged(typeof(Translation));
    30.  
    31.             _boundsQuery = EntityManager.CreateEntityQuery(ArchetypesLogic.LevelBounds); //
    32. ComponentType.ReadOnly<LevelBounds>()
    33.  
    34.             RequireSingletonForUpdate<LevelBounds>();
    35.         }
    36.  
    37.         protected override JobHandle OnUpdate(JobHandle inputDeps) {
    38.             LevelBounds bounds = _boundsQuery.GetSingleton<LevelBounds>();
    39.             return new ClampToBoundsJob(bounds).Schedule(this, inputDeps);
    40.         }
    41.  
    42.         [BurstCompile]
    43.         private struct ClampToBoundsJob : IJobForEach<Translation> {
    44.             private readonly float2 _min;
    45.             private readonly float2 _max;
    46.  
    47.             public ClampToBoundsJob(LevelBounds bounds) {
    48.                 _min = bounds.Min;
    49.                 _max = bounds.Max;
    50.             }
    51.  
    52.             public void Execute(ref Translation trm) {
    53.                 float3 pos = trm.Value;
    54.  
    55.                 pos.x = math.clamp(pos.x, _min.x, _max.x); // Y -> Z due to orientation
    56.                 pos.z = math.clamp(pos.z, _min.y, _max.y); // Y -> Z due to orientation
    57.  
    58.                 trm.Value = pos;
    59.             }
    60.         }
    61.     }
    62. }
    An entity that is queried is only prb_Game Camera (I've tested it by retrieving entity array + outputting their names):
    upload_2019-7-30_16-37-21.png

    upload_2019-7-30_16-39-19.png
     
  3. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,270
    _boundsQuery is what is triggering your system to run. Queries use || logic by default when determining if a system should run.
     
  4. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    You mean the singleton one via RequireSingletonForUpdate?
     
  5. TRS6123

    TRS6123

    Joined:
    May 16, 2015
    Posts:
    246
    RequireForUpdate(EntityQuery query)
     
  6. DreamingImLatios

    DreamingImLatios

    Joined:
    Jun 3, 2017
    Posts:
    4,270
    This one was the red flag for me.
    Code (CSharp):
    1. _boundsQuery = EntityManager.CreateEntityQuery(ArchetypesLogic.LevelBounds);
    I think the singleton generates the same query, but don't quote me on that since I don't use Unity's singleton API and don't actually know what the behavior is off the top of my head.
     
  7. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    So I've removed RequireSingletonForUpdate<LevelBounds>(); and it still runs.
    Also, bounds query is not added to the system as a dep. Its being created via entity manager only for fetching singleton.

    After short run through the code it seems like GetEntryQuery adds query as dependency, EntityManager.CreateQuery<...> does not.

    So idk at this point.

    Would be cool to see some kind of debug tool to check whats invalidates the chunk or triggers the filter.
     
  8. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I'm slightly confused by your question.

    Is your issue your _clampQuery still returning chunks even when SetFilterChanged(typeof(Translation)) is used? And you just want to know which system triggered it?

    If so, just to check, do you know/understand how SetFilterChanged works? (for example no entity needs to actually change for this to return a chunk)

    It'd be very rare for a solution using SetFilterChanged(typeof(Translation)) to simply not return all chunks all the time.
     
    Last edited: Jul 31, 2019
  9. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    I want to not run a system at all, unless Translation component changes.

    Unfortunately, it keeps running even when nothing (as it seems) modifies the translation.

    - Edit -
    By modifies I mean system that touches entities with a write access.
     
    Last edited: Jul 31, 2019
  10. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    I'm referring this:
    https://docs.unity3d.com/Packages/com.unity.entities@0.0/manual/component_group.html#change-filters
    But there's no way to check what system has ran on those.
     
  11. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Code (CSharp):
    1. The solution is RequireForUpdate(_clampQuery);
    However, this will still likely run every frame as _clampQuery will probably still return every transform. I'm not sure you understand how SetFilterChange works.

    SetFilterChange does not filter out entities that have changed, it filters chunks that have potentially changed.
    It does this by simply looking if any Query has a ComponentType.ReadWrite<Translation>
    Even if no Translation in this query ever changes, it will still mark every chunk as changed.

    To quote the docs you just listed

    So as you probably have at least 1 system somewhere in your project that changes the Translation it's always going to dirty all chunks.
     
  12. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    That's exactly what I'm trying to find out. What system modifies that chunk.

    Hmmm. Could it be that system's own query triggers the chunk to be changed?
    Because clamp system uses RW access on Translation.

    But that would render filter somewhat useless in this case.
     
  13. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Pretty certain RW on a system that is filtering will trigger itself.

    For figuring out what other system is triggering it though, you can just quickly look through entity debugger and see systems with write access to transform that are not inactive.
     
    xVergilx likes this.
  14. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    So systems like this will still mark chunks as write accessed, or those will be marked only when that specific query runs?
    upload_2019-7-31_12-15-44.png
     
  15. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    Yep those 13 entities chunks will be marked as write accessed every frame.

    One big issue with physics package at the moment is actually that it forcefully adds back Translation/Rotation to static entities. The static optmized entities don't have Translation/Rotation component, instead only LocalToWorld and because of this you can do a SetFilter(typeof(LocalToWorld)) and it will ignore all static components in your project.

    However even if your physics objects are static, the physics package relies on Translation/Rotation so it adds them back during conversion. This means all those static components are now process every frame in TransformSystem adding a lot of overhead. Also this means the LocalToWorld is now marked as changed every frame even though they should be static components.

    The physics system really needs to stop using Translation/Rotation for static components (basically the exact query you're looking at there) and instead use LocalToWorld.
     
    xVergilx likes this.
  16. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    Got it, I guess I need to filter it by something else.
     
  17. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    One more question @tertle , is it worth doing something like this:
    1. Adding tag component to entities that are modified, e.g. TranslationModified via EntityManager.AddComponent + Query.
    2. Using EntityQuery + All + TranslationModified as a filter instead for the systems?
    3. Removing tag component at the end of the frame.

    Or that will cause too much chunk fragmentation?
     
  18. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    If I need to do work on a collection of entities only if they have changed I usually just add a component that stores previous state and brute force it.

    For example this system here that only does work if the xz position has changed by a tolerance level in an IJobChunk (threw in some comments).

    Code (CSharp):
    1.         /// <inheritdoc />
    2.         public void Execute(ArchetypeChunk chunk, int chunkIndex, int firstEntityIndex)
    3.         {
    4.             // Filters out static entities
    5.             if (!chunk.DidChange(this.LocalToWorldType, this.LastSystemVersion))
    6.             {
    7.                 return;
    8.             }
    9.  
    10.             var dynamicObstructions = chunk.GetNativeArray(this.DynamicObstructionType);
    11.             var localToWorlds = chunk.GetNativeArray(this.LocalToWorldType);
    12.  
    13.             for (var i = 0; i < chunk.Count; i++)
    14.             {
    15.                 var obstruction = dynamicObstructions[i];
    16.                 var localToWorld = localToWorlds[i];
    17.  
    18.                 var position = localToWorld.Position.xz;
    19.  
    20.                 // HAS CHANGED (with some tolerance)
    21.                 if (math.lengthsq(obstruction.PreviousPosition - position) < this.ToleranceSq)
    22.                 {
    23.                     continue;
    24.                 }
    25.  
    26.                 // store previous position
    27.                 obstruction.PreviousPosition = position;
    28.                 dynamicObstructions[i] = obstruction;
    29.  
    30.                 // DO WORK HERE
    31.                this.Impl.Execute(position);
    32.             }
    33.         }
     
    xVergilx likes this.
  19. tertle

    tertle

    Joined:
    Jan 25, 2011
    Posts:
    3,761
    I should add, I only do this if the work is heavy or the work has a state where running it multiple times will yield different results.

    If the job is just doing some type of check like clamping to bounds etc I'll just run it every frame regardless if it's changed. Burst is so fast.
     
    xVergilx likes this.