Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Question FixedString.ToString() in Entities.ForEach()

Discussion in 'Entity Component System' started by turick00, Sep 11, 2020.

  1. turick00

    turick00

    Joined:
    Jul 14, 2020
    Posts:
    31
    Hi all -- I have lots of string data. I've been using FixedString for a bit now and it's worked well, however, if I try to iterate over a set of entities and grab the value, I get the error:

    error DC0002: Entities.ForEach Lambda expression invokes 'ToString' on a EtlFetcherSystem which is a reference type. This is only allowed with .WithoutBurst() and .Run(). And the suspect line of code is doing just that... calling ToString() on one of my component's FixedString32 fields.

    I really didn't want to post a question... I wanted to solve this on my own. But I'm an hour in and not making progress! What is the right approach to handle strings? Should I just be converting them to some sort of blittable array? I thought maybe I could try calling `GetComponentDataFromEntity`, which if it works, seems like a very odd workaround to just using the component reference from the query? I don't know... seems like a foundational piece of info and I just can't seem to find the best practice...
     
  2. burningmime

    burningmime

    Joined:
    Jan 25, 2014
    Posts:
    845
    What are you using the value for? You can generally leave it in FixedString.
     
  3. turick00

    turick00

    Joined:
    Jul 14, 2020
    Posts:
    31
    Having all kinds of issues now :(

    I am displaying the position of about ~160 satellites around the earth. Each satellite has some metadata attached to it:

    https://ephemerides.planet-labs.com/0e0f_oem.txt

    So, I have satellite IDs, timestamps, various names and values, etc etc. My struct very simply reflects the metadata rows in that text file:

    Code (CSharp):
    1.     public struct EtlHeaderData : IComponentData
    2.     {
    3.         public FixedString32 ETag;
    4.         public FixedString32 CreationDate;
    5.         public FixedString32 Originator;
    6.         public FixedString32 ObjectName;
    7.         ...
    8.     }
    My system hates me. I want to check every hour to see if there is new data available:

    Code (CSharp):
    1.  protected override void OnUpdate()
    2.         {
    3.            
    4.             _lastUpdate += UnityEngine.Time.fixedDeltaTime;
    5.             if (Mathf.Abs(_lastUpdate - _executionInterval) > Tolerance)
    6.             {
    7.                 return;
    8.             }
    9.            _lastUpdate = 0f;
    10.  
    11.             Entities
    12.                 .ForEach((ref EtlHeaderData headerData) =>
    13.                 {
    14.                    
    15.                     string url = EphemerisURL + headerData.ObjectId.ToString() + EphemrisFileSuffix;
    16.                 ..
    17.  
    Actually, I was originally using string.Format, which required me to call ToString(), but it complained about my use of ToString(). I found that I can actually build that string without calling ToString()

    Code (CSharp):
    1.  
    2. string url = EphemerisURL + headerData.ObjectId + EphemrisFileSuffix;
    3.  
    But now I feel like everything I do tells me I'm invoking something on a reference type. The call to Mathf.Abs says my lambda is invoking "Concat" on my system, which is a reference type. I also can't break my code up into methods, which I guess makes sense since the lambda needs to capture everything, unless there is some other technique... making them all lambdas? Clearly I'm new to this :) All of this info seems like ECS 101, but I'm really struggling finding the answers.
     
  4. nyanpath

    nyanpath

    Joined:
    Feb 9, 2018
    Posts:
    77
    Have you tried Unity.Mathematics so you can use math.abs() instead of Maths.Abs()?
     
  5. turick00

    turick00

    Joined:
    Jul 14, 2020
    Posts:
    31
    So granted I'm new to Unity, but I have written several working systems. This was blowing my mind, because it was nearly the same error on 4 completely different parts of the code doing completely different things. I could not believe that I couldn't call ToString!

    8 hours in to searching, trying everything I could think of to work around the issue, restarting unity, etc, and suddenly the error went away. The only real change was I added an Entity parameter to my ForEach lambda. All the errors are gone, Math and Mathf abs works, ToString works, method calls work. I'm baffled, and even though I made no progress today, it works! So I'm happy
     
  6. Zec_

    Zec_

    Joined:
    Feb 9, 2017
    Posts:
    148
    Coding with Burst (a pillar stone in Unity's ECS) becomes far easier with some basic understanding on the difference between Reference Types and Value Types in C#. The simplest way of thinking about it is:
    • Classes are Reference Types
    • Structs are Value Types
    • If you cast a Struct to the object type or to an interface type, boxing occurs and the struct becomes stored as a Reference Type.
    Diving even further into how it's stored in memory explains the reason to why it is like this, but I unfortunately have to rush and don't have the time to explain it. There's tons of materials on this online though! Some keywords would be:
    • Reference Types vs Value Types
    • Stack vs Heap allocation (slightly more advanced topic, explains how the memory is stored)
    So, returning to your errors. Burst only supports Value Types. So anything that is a reference type will cause Burst compilation in Unity to throw errors when compiling. Jobs (Entities.ForEach and the like) are automatically tagged for Burst compilation, but you can disable it with .WithoutBurst() if you really need to use reference types.

    String in itself is a class, which is a reference type. Unity implemented FixedString and all their other native collections as structs to be able to store and use their data in Burst+Jobs. This is why you received the "reference type" errors when using ToString().

    Your other errors yesterday seems to have stemmed from that you were using Mathf.Abs. I believe a lot of functions in this older Unity API often triggers another kind of errors in Burst due to it's usage of static variables internally, which burst doesn't support. Their newer and modern math API is the one that exists in the Unity.Mathematics namespace, the math class.

    I hope this gives you some help along the way. If you need to know if a type is a class or a struct, you can right-click them in Visual Studio and "Go to Definition" to see how they're defined!

    Edit: I re-read the thread a little bit later and realize that you might already have a basic grasp about this topic. I saw some comment about being new to this, so I might have assumed too much. Apologies if I dumbed this down too much!
     
    Last edited: Sep 12, 2020
    DavidLe360 and turick00 like this.