ECS is DOD but UIToolkit is OOP. How do you bridge this divide?

ECS is DOD but UIToolkit is OOP. How do you bridge this divide?

For example, DynamicBuffer is a list and ListView is meant to display those. Problem arise immediately:

  • DynamicBuffer does not implement System.Collections.IList
  • DynamicBuffer is made invalid by some structural changes (these that result is buffer relocation)

First issue can be patched over by creating an adapter struct. But it will throw exceptions eventually because of this second point for which I found no 100% reliable solution so far.

IList adapter example

using System.Collections;
using UnityEngine;
using Unity.Collections;

/// <summary> note: nativeCollection.AsNativeArray() exists so you can pass most native container types here </summary>
public struct NativeArrayAsIList <T> : IList where T : unmanaged
{
    public NativeArray<T> array;
    public NativeArrayAsIList ( NativeArray<T> arr ) => this.array = arr;
    object IList.this[int index]
    {
        get => this.array[index];
        [System.Obsolete("read-only",true)] set => this.array[index] = (T)value;
    }
    public int Count => this.array.Length;
    public bool IsReadOnly => true;
    public bool IsFixedSize { get{ Debug.LogWarning("IsFixedSize not implemented"); return false; } }
    public bool IsSynchronized { get{ Debug.LogWarning("IsSynchronized not implemented"); return false; } }
    public object SyncRoot { get{ Debug.LogWarning("SyncRoot not implemented"); return null; } }
    [System.Obsolete("read-only",true)] public int Add ( object value ) { Debug.LogWarning($"Add( {value} )"); return -1; }
    [System.Obsolete("read-only",true)] public void Clear () => Debug.LogWarning($"Clear()");
    [System.Obsolete("not implemented")] public bool Contains ( object value ) { Debug.LogWarning("Contains not implemented"); return false; }
    [System.Obsolete("read-only",true)] public void CopyTo ( System.Array array , int index ) => Debug.LogWarning("CopyTo not implemented");
    [System.Obsolete("not implemented")] IEnumerator IEnumerable.GetEnumerator () { Debug.LogWarning("IEnumerable.GetEnumerator not implemented"); return this.array.GetEnumerator(); }
    [System.Obsolete("read-only",true)] public void RemoveAt ( int index ) => Debug.LogWarning("RemoveAt not implemented");
    [System.Obsolete("not implemented")] public int IndexOf ( object value ) { Debug.LogWarning("IndexOf not implemented"); return -1; }
    [System.Obsolete("read-only",true)] public void Insert ( int index , object value ) => Debug.LogWarning("Insert not implemented");
    [System.Obsolete("read-only",true)] public void Remove ( object value ) => Debug.LogWarning("Remove not implemented");
}

Any ideas, thought, examples, alternatives?

1 Like

Structural changes can be either avoided or you can copy DynamicBuffer to NativeArray and work with a copy of data instead.

I haven’t worked with UI Toolkit & Entities simultaneously, because uGUI is still going strong.
And updating component objects work just fine from SystemBase.

But in general you’d either avoid structural changes when processing data that can potentially cause structural changes and defer them. Or work over copy of data.

1 Like

My initial approach was just that as well - to copy this buffer. But because this data presented to player was not always 100% up to date it resulted in player input actions over invalid or non-existent data - like using an inventory item that no longer exists.

Thanks! Avoiding structural changes haven’t even crossed my mind. Pre-allocating larger buffers might be a solution although it will come with a memory cost and buffer size cap potentially. I will try that.

I have a SystemBase with a query that’s operating on a change filter. Once changes occur I create a new array of view model data and bind that to the ListView.
It’s working well and I haven’t looked into optimizing this (yet) as it’s not showing up any concerns in the profiler.

Having a 1:1 binding would be great of course but as long as UIToolkit can’t operate on unmanaged memory this is a far off dream.

3 Likes