friflo / Friflo.Engine.ECS

C# ECS 🔥 high-performance
https://friflo.gitbook.io/friflo.engine.ecs
GNU Lesser General Public License v3.0
136 stars 9 forks source link

Strange Query Witch Iterates Twice With Single Entity #24

Open sergiyha opened 13 hours ago

sergiyha commented 13 hours ago

Hi. Please, don't ask me what the hell am I doing with this junk of code) This code represents the execution flow error. Maybe it is not an error and we cannot do things like that but anyway, I am here to show what I've found because it is strange.

The actual problem is that "Debug.LogError(_query.Chunks.Count + " Second Log");" shouldn't be executed twice but it is. Namely, if we do the same thing for a second time. As you can see "Debug.LogError(_query.Chunks.Count + " First Log");" is invoked only once.

FriFlo version: 2.0.0 Environment: Unity Editor 2022.3.47f1

using Friflo.Engine.ECS;
using UnityEngine; 

public class Test : MonoBehaviour
{
    // Start is called before the first frame update
    void Start()
    {
        Exe();
    }

    public struct Comp_1 : IComponent
    {
    }

    public struct Comp_2 : IComponent
    {
    }

    public struct Comp_3 : IComponent
    {
    }

    public struct Comp_4 : IComponent
    {
    }

    public static void Exe()
    {
        var store = new EntityStore();
        var _query = store.Query<Comp_1>()
            .WithoutAllComponents(ComponentTypes.Get<Comp_2>());

        var cb = store.GetCommandBuffer();
        var brandNewEntt = store.CreateEntity();
        brandNewEntt.AddComponent<Comp_1>();
        cb.ReuseBuffer = true;

        foreach (var (c1, e) in _query.Chunks)
        {
            Debug.LogError(_query.Chunks.Count + " First Log");
            for (int i = 0; i < e.Length; i++)
            {
                var enttId = e[i];
                cb.AddComponent<Comp_2>(enttId);

                var newCB = store.GetCommandBuffer();
                newCB.AddComponent(enttId,new Comp_3());
                newCB.AddComponent(enttId, new Comp_4());
                newCB.Playback();
            }
        }
        cb.Playback();

        brandNewEntt.DeleteEntity();
        brandNewEntt = store.CreateEntity();
        brandNewEntt.AddComponent<Comp_1>();

        foreach (var (c1, e) in _query.Chunks)
        {
            Debug.LogError(_query.Chunks.Count + " Second Log");
            for (int i = 0; i < e.Length; i++)
            {
                var enttId = e[i];
                cb.AddComponent<Comp_2>(enttId);

                var newCB = store.GetCommandBuffer();
                newCB.AddComponent(enttId,new Comp_3());
                newCB.AddComponent(enttId, new Comp_4());
                newCB.Playback();
            }
        }

        cb.Playback();
    }
}

image

friflo commented 2 hours ago

Hi @sergiyha,

this query behavior is expected. A query internally returns the chunks of all matching Archetypes. The first loop has one the second loop has two matching Archetypes. The second loop changes the Chunks of _query.Archestypes[1]. See: https://github.com/friflo/Friflo.Engine.ECS/blob/main/src/Tests/ECS/Github/Test_GitHub_24.cs#L65

Possible fixes:

Fix 1 https://github.com/friflo/Friflo.Engine.ECS/blob/main/src/Tests/ECS/Github/Test_GitHub_24.cs#L108 This version returns only the matching entities. This list is independent from Archetype changes.

Fix 2 (preferred) Execute newCB.Playback(); after the loop has finished. This ensures the Archetypes remain unchanged within the loop. This is the intended way to use command buffers.