Writing data to chunks SharedComponent via unsafe code gives unexpected results

I am currently making a generic World Serializer and im having issues reapplying SharedComponentData.

 var dynamicHandle = m.GetDynamicSharedComponentTypeHandle(componentType);

 unsafe
 {
     var chunkDataPtr = chunk.GetDynamicSharedComponentDataAddress(ref dynamicHandle);
     var serializedDataPtr = blob.data.GetUnsafePtr();
     UnsafeUtility.MemCpy(chunkDataPtr, serializedDataPtr, blob.data.Length);
 }

Im using this piece of code to simply copy the binary data into the chunks SharedComponent and the following to Debug the it.


var typeHandle = m.GetSharedComponentTypeHandle<RuntimeItemData>();
var itemRef = chunk.GetSharedComponent(typeHandle).itemRef;
UnityEngine.Debug.Log(itemRef);
unsafe
{
    var ePtrs = chunk.GetEntityDataPtrRO(m.GetEntityTypeHandle());

    for (int i = 0; i < chunk.Count; i++)
    {
        UnityEngine.Debug.Log(*(ePtrs + i));
        UnityEngine.Debug.Log(m.GetSharedComponent<RuntimeItemData>(*(ePtrs + i)).itemRef);
    }
}

Which results in the following.

Checking the data on a later update results in the entity and chunk DataAccessVariant returning the same invalid data (which the entity inspector reflects).

  • Am i missing something?
  • Why does accessing a shared component through an entity vs a chunk yield different result EVER?(this seriously undermines my understanding of ecs)
  • Am i using the unsafe chunk api wrong
  • and if so how do i properly reapply the binary data?

Thanks in advance

I think there’s a problem with how you copy it. See, you’re just overwriting existing data, not going through the API, so entities are not moved around and grouped with the new sharedComp data.

Go through the API instead.

Why the first entity in your loop outputs data and the 2nd doesn’t, I’m not sure. Both should have the same SharedComp as they exist in the same chunk.

Thanks for the reply.

Overwriting existing data (or rather the lack there of) is the point. I create the entities with the a saved archetype, ensure theyre in the same chunks by giving them a shared component with the chunks index and then try to paste the serialized data into the chunk (which at this point has no data what so ever).

The safe api doesnt have (as far as im aware) a way to dynamicly set unmanaged sharedcomponentdate and i really would like to avoid the overhead of the managed stuff.

Also just for clarification the first debug statement is getting the data through the Chunk with a type handle and the following is from the first entity.

I think im on the right track with the unsafe api and im just doing something wrong.

At least use

public void SetSharedComponentData_Unmanaged(
            Entity entity,
            TypeIndex typeIndex,
            void* componentData,
            void* componentDataDefaultValue,
            in SystemHandle originSystem = default)

from EntityDataAccess. You need internals to get access to EntityDataAccess. AsmRef can do the trick if you are familiar.

Not sure what you mean with overhead of managed stuff. There’s no managed memory involved here. TBH, doing this untyped is making this unnecessarily complicated. If you want to make a batch call, just call EntityManager.SetSharedComponent with an EntityQuery. This will be faster than your invidiual memcpy’s. Also there’s a structural change involved so you have to do it on the main thread.

Bulk memcpy makes more sense for IComponentData.

Thanks again for taking the time.

Tho using an internal api (which is prone to undocumented change) could work i dont see how thats any different or better than using the available api.

I cant use typed api (SetSharedComponent) because the type isnt known at compile time since im trying to create a non specific serialization system.

Also i dont know why using setsharedcomponent, which to my understanting also just iterates the chunks to copy data, would be faster than a memory native approach.

Do you know about stableTypeHashes? You can get one with TypeHash.CalculateStableTypeHash. As long as the type is stable, this hash will stay the same and you can get the pretty much every data you need about it with helper methods:

var typeIndex = TypeManager.GetTypeIndexFromStableTypeHash(stableTypeHash);
var typeInfo = TypeManager.GetTypeInfo(typeIndex);

For any untyped route this is the way to go imo.

From your code it wasn’t clear to me what you do outside of it. Indeed there’s nothing else happening what you describe but why invent the wheel when there are already optimized methods available, right?

There’s quite a lot going on to setup shared component data. Best to take a look at the source itself. It ends up in UnsafeUtility.MemCpy but there’s way more than just copying data around and my best guess it’s the reason why it fails.

Looking at the GetSharedComponent method. One reason it can return a default(T) is when the sharedComponentIndex is 0. So best to debug and make sure if that’s the case.
Talking about this one:

public T GetSharedComponentData_Unmanaged<T>(Entity entity) where T : unmanaged, ISharedComponentData
{
    var typeIndex = TypeManager.GetTypeIndex<T>();
    UnityEngine.Assertions.Assert.IsFalse(TypeManager.IsManagedSharedComponent(typeIndex));
    EntityComponentStore->AssertEntityHasComponent(entity, typeIndex);

    var sharedComponentIndex = EntityComponentStore->GetSharedComponentDataIndex(entity, typeIndex);
    return GetSharedComponentData_Unmanaged<T>(sharedComponentIndex);
}

edit: btw are you sure you really need a sharedComponent? Using one is very unusual, especially looking at that case. I don’t really see the benefit in fragmenting chunks because of it.

Good pointer. Indeed all the chunks SharedComponentIndex remain zero. Looked through the documentation again and concluded that you cant simply memcpy shared componentdata to chunks (because chunks DONT actually contain their respective SharedComponent but share them with others) and setting it properly is more complicated.

I guess dynamicly setting SharedComponentData is not something the current api supports

yeah, i might have gotten a bit carried away with shared components.

Thanks again for your help