Why there is no DidChange method in BufferFromEntity<T>?

I see ComponentDataFromEntity has DidChange() but BufferFromEntity doesn’t. Does anybody know a specific reason for this? Also I noticed that ArchetypeChunk has DidChange() will happily accept T derived from IBufferElementData, but this doesn’t help in the situations where I have to use BufferFromEntity, i.e. where I am reaching to another entity’s buffer.
Any advice on how I can change-check dynamic buffers?

No reason, we probably just forgot to add it in both places. Good catch, I’ll take care of that right now!

1 Like

For those interested, this has been addressed in v.0.12.0, thank you @cort_of_unity !

Just noticed something - even though DidChange() was added to dynamic buffers, WithChangeFilter() still doesn’t work properly with dynamic buffers, in my case it basically reports as always changed

If I’m not mistaken, that’s because getting a buffer (even in ReadOnly) always marks it as a Read/Write. Not sure if it’s a bug or a technical limitation I don’t understand.

I modified the Entities package for one of my projects and added a true GetBufferReadOnly method. It seems to work fine at the moment.

1 Like

Thank you @msfredb7 . Can you share the code for the true read only buffer access method?

From the Git diff, I think that’s all the changes related to my GetReadOnlyBuffer:

NB:

  • My changes were done based on Entities 0.10.0, so they might not work out of the box for higher versions.
  • This might very well expose bugs as I haven’t tested all use cases
  • Remember that this is not code officially supported by Unity. I suggest you rely on it as little as possible.

Unity.Entities/EntityManagerAccessComponentData.cs, line 305

// ADDED BY FBESSETTE 2020-04-09
public DynamicBuffer<T> GetBufferReadOnly<T>(Entity entity) where T : struct, IBufferElementData
{
    var typeIndex = TypeManager.GetTypeIndex<T>();
    var ecs = GetCheckedReadEntityDataAccess();

#if ENABLE_UNITY_COLLECTIONS_CHECKS
    var safetyHandles = &ecs->DependencyManager->Safety;
#endif

    return ecs->GetBufferReadOnly<T>(entity
#if ENABLE_UNITY_COLLECTIONS_CHECKS
        , safetyHandles->GetSafetyHandle(typeIndex, true),
        safetyHandles->GetBufferSafetyHandle(typeIndex)
#endif
    );
}

Unity.Entities/EntityManager.cs, line 73

// ADDED BY FBESSETTE 2020-05-24
internal EntityDataAccess* GetCheckedReadEntityDataAccess()
{
#if ENABLE_UNITY_COLLECTIONS_CHECKS
    AtomicSafetyHandle.CheckReadAndThrow(m_Safety);
    if (m_JobMode != m_EntityDataAccess->m_JobMode)
    {
        throw new InvalidOperationException($"EntityManager cannot be used from this context job mode {m_JobMode} != current mode {m_EntityDataAccess->m_JobMode}");
    }
#endif
    return m_EntityDataAccess;
}

Unity.Entities/ExclusiveEntityTransaction.cs, line 133

// added by fbessette 2020-04-19
public DynamicBuffer<T> GetBufferReadOnly<T>(Entity entity) where T : struct, IBufferElementData
{
    return m_Manager.GetBufferReadOnly<T>(entity);
}

Unity.Entities/EntityDataAccess.cs, line 877

// ADDED BY FBESSETTE 2020-04-19
public DynamicBuffer<T> GetBufferReadOnly<T>(Entity entity
#if ENABLE_UNITY_COLLECTIONS_CHECKS
    , AtomicSafetyHandle safety, AtomicSafetyHandle arrayInvalidationSafety
#endif
    ) where T : struct, IBufferElementData
{
    var typeIndex = TypeManager.GetTypeIndex<T>();

#if ENABLE_UNITY_COLLECTIONS_CHECKS
    EntityComponentStore->AssertEntityHasComponent(entity, typeIndex);
    if (!TypeManager.IsBuffer(typeIndex))
        throw new ArgumentException(
            $"GetBuffer<{typeof(T)}> may not be IComponentData or ISharedComponentData; currently {TypeManager.GetTypeInfo<T>().Category}");
#endif

    if (IsMainThread)
        DependencyManager->CompleteReadAndWriteDependency(typeIndex);

    BufferHeader* header =
            (BufferHeader*)EntityComponentStore->GetComponentDataWithTypeRO(entity, typeIndex);

    int internalCapacity = TypeManager.GetTypeInfo(typeIndex).BufferCapacity;

#if ENABLE_UNITY_COLLECTIONS_CHECKS
    var useMemoryInit = EntityComponentStore->useMemoryInitPattern != 0;
    byte memoryInitPattern = EntityComponentStore->memoryInitPattern;
    return new DynamicBuffer<T>(header, safety, arrayInvalidationSafety, true, useMemoryInit, memoryInitPattern, internalCapacity);
#else
    return new DynamicBuffer<T>(header, internalCapacity);
#endif
}

Thanks @msfredb7 . I do realize this is not officially maintained code, I’m just stuck with my dynamic buffers constantly becoming overwritten (without this actually happening). In my case this leads to meshes being rebuilt every frame :frowning: