#Avoid using generic jobs if possible

1 messages · Page 1 of 1 (latest)

rugged plover
#

I have generic job which takes arbitrary component with known data layout, reinterpret it to T array and copy to output array (which is ComputeBuffer by the way). What I want is to try to avoid using generic job here, because of need to register them for every component case.

Can job below avoid using generic in any way? All it does actually just copy one array to another with same size and same data layout.

[BurstCompile]
    // takes all chunks by query, and then just copy component data to compute buffer starting from 1st entity in query index
    internal struct SyncPropertyByQueryJob<TProperty> : IJobChunk
            where TProperty : unmanaged
    {
        [ReadOnly][DeallocateOnJobCompletion] public NativeArray<int> chunkBaseEntityIndices;
        // this should be filled every frame with GetDynamicComponentTypeHandle
        [ReadOnly]public DynamicComponentTypeHandle componentTypeHandle;
        public int typeSize;
        [WriteOnly][NativeDisableParallelForRestriction] public NativeArray<TProperty> outputArray;

        public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
        {
            var data = chunk.GetDynamicComponentDataArrayReinterpret<TProperty>(ref componentTypeHandle, typeSize);
            NativeArray<TProperty>.Copy(data, 0, outputArray, startCopyToIndex, data.Length);
        }
    }
severe fable
#

you could just pass a void* from NativeArray.GetUnsafePtr() into each job and UnsafeUtility.MemCpy() it over

rugged plover
#

@severe fable thank you for answer, but I'm very unfamiliar with all this pointer stuff. Can you please show quick example of such a job?

severe fable
#

actually looking at it more, i don't see how you'd know which chunks to copy from if you don't use a generic. i didn't read close enough to notice that at first, and just read your text

rugged plover
#

@severe fable but if chunk could return ptr to use it with MemCpy, then how code should look like?

severe fable
#

well but it can't, because you don't know where in the chunk the data array for your particular component is, since chunks can have more than one component type in them.

tardy sun
#

You can totally avoid generics here, just switch it to a byte array and just pass in the size of your data

#

This is basically what I do for my save system

#

The only thing in your code that is the generic is the output array

rugged plover
tardy sun
#

Use byte there as well

rugged plover
#

as T?

tardy sun
#

Yeah sec let me find a pc

#

I use dynamic type handles a lot to replace generic jobs

#

But I forgot pattern

rugged plover
#

ok, I see now. But how to convert any array to byte array?

severe fable
#

and here we show that i haven't used dynamic component type handles before...

rugged plover
#

Reinterpret?

tardy sun
#
var components = chunk.GetDynamicComponentDataArrayReinterpret<byte>(ref this.ComponentType, this.ElementSize);
Check.Assume(components.Length > 0);
this.Serializer.AddBufferNoResize(components);```
#

Yeah from my save system

#

Serializer is just a native list

#

that's from here if you want an example

#

you already pass in type size so you don't even need to change anything you're passing

#
[BurstCompile]
// takes all chunks by query, and then just copy component data to compute buffer starting from 1st entity in query index
internal struct SyncPropertyByQueryJob : IJobChunk
{
    [ReadOnly][DeallocateOnJobCompletion] public NativeArray<int> chunkBaseEntityIndices;
    // this should be filled every frame with GetDynamicComponentTypeHandle
    [ReadOnly]public DynamicComponentTypeHandle componentTypeHandle;
    public int typeSize;
    [WriteOnly][NativeDisableParallelForRestriction] public NativeArray<byte> outputArray;

    public void Execute(in ArchetypeChunk chunk, int unfilteredChunkIndex, bool useEnabledMask, in v128 chunkEnabledMask)
    {
        var data = chunk.GetDynamicComponentDataArrayReinterpret<byte>(ref componentTypeHandle, typeSize);
        NativeArray<byte>.Copy(data, 0, outputArray, startCopyToIndex, data.Length);
    }
}```

when scheduling job
outputArray = previousOutputARray.Reinterpret<byte>(sizeof(TProperty))
rugged plover
#

Ok, so I can try to use ComputeBuffer.BeginWrite<byte>() with any actual data inside and chunk.GetDynamicComponentDataArrayReinterpret<byte>(ref this.ComponentType, this.ElementSize); also with any actual data inside and then just copy without any generic

#

One thing still confusing me. Why we're passing stride of our actual data layout?

tardy sun
#

the typeSize?

rugged plover
#

yes

tardy sun
#

🤷‍♂️ DynamicComponentTypeHandle doesn't have the info

#

they could look it up and store it inside when created though

rugged plover
#

I'm more about: we reinterpreting as byte but passing another type's size

tardy sun
rugged plover
#

oh, ok

tardy sun
#

which is going to be sizeOfElement * length / sizeof<T>

#

T is byte so 1

#

but you know

#

looking at source, it just reads this from the chunk anyway 😄

#

CheckComponentSizeMatches(typeHandle, typeSize, expectedTypeSize);
it's only used to confirm it matches

#

var typeSize = archetype->SizeOfs[typeIndexInArchetype];
it doesn't actually use it for the array length, just reads it directly

#

oh well

rugged plover
#

WOW! It works!!!! Just a little mind work with indecies and it WORKS!

#

@tardy sun @severe fable thank you for help, it will improve my system a lot!

peak lava
#

Could someone explain IJobChunk for dummies?

tardy sun
#

Ijc is basically the way you iterate entities in jobs

#

Things like IJobEntity code gen into ijc to provide a more convenient api

#

I'm not a fan of most tutorials because they skip over this, personally when teaching new devs we start with ijc to understand how entities work then move onto the code gen convenience

#

Anyway the manual will do a better job explaining how to iterate than I will on a phone