Sunday, January 5, 2020

Unity DOTS Life Tutorial entities 0.4.0 preview.10 #2 Worker Threads - GameDev.net

This tutorial covers changing the jobs from the first tutorial to be scheduled on worker threads. 

Build tested on entities 0.4.0 preview.10 . Packages used are shown here

 The source is at https://github.com/ryuuguu/Unity-ECS-Life.git and is commit Tutorial Worker Threads f2 on Jan 2 30, 2020. The Zip file of the project is here.  

Warning: DOTS is still bleeding edge tech. Code written now will not be compatible with first release version of DOTS. There are people using the current version DOTS for release games, but they usually freeze their code to specific package and will not be able use future features without rewriting existing code first to compile and run with newer versions. So if you need 10,000 zombies active at once in your game this is probably the only way to do it. Otherwise this tutorial is a good introduction if you think you will want to simulate thousands of entities in the future or just want a get a feel for code this type of thing in Unity.

To change to the code to be multithreaded only the Systems need changes no changes are needed to the Entities or Components in this case. First I will change the UpdateLiveSystem since it is a very simple change. The line "var job = " was added. The line "}).Run();" was changed to "}).Schedule(inputDeps);" this causes the job to be scheduled on worker threads and causes inputDeps to be returned where the first change assigns it to job.

Finally "return default;" is changed to "return job;" to return the inputDeps.

public class UpdateLiveSystem : JobComponentSystem
{
    protected override JobHandle OnUpdate(JobHandle inputDeps)
    {
        float zDead = ECSGrid.zDead;
        float zLive = ECSGrid.zLive;
        
        var job = Entities
            .WithChangeFilter<NextState>()
            .ForEach((ref Live live,ref Translation translation, inNextState nextState) => {
                live.value = nextState.value;
                translation.Value = new float3(translation.Value.x, translation.Value.y,
                math.select( zDead,zLive, live.value == 1));
            }).Schedule(inputDeps);

        return job;
    }
}

The UpdateLiveSystem system was easy to change because it is self contained and references nothing out the single entity being processed by the foreach. The GenerateNextStateSystem needs more work to change because it needs to access 8 other entities. It only needs to read from them so it is still possible for this job to run multithreaded. Here is the old code.

protected override JobHandle OnUpdate(JobHandle inputDeps)
{
    int[] stay = new int[9];
    int[] born = new int[9];
    stay[2] = stay[3] = 1; // does NOT include self in count
    born[3] = 1;

    var liveLookup = GetComponentDataFromEntity<Live>();

    Entities.WithoutBurst().ForEach((ref NextState nextState, in Live live, in Neighbors neighbors) => {
        int numLiveNeighbors = 0;

        numLiveNeighbors += liveLookup[neighbors.nw].value;
        numLiveNeighbors += liveLookup[neighbors.n].value;
        numLiveNeighbors += liveLookup[neighbors.ne].value;
        numLiveNeighbors += liveLookup[neighbors.w].value;
        numLiveNeighbors += liveLookup[neighbors.e].value;
        numLiveNeighbors += liveLookup[neighbors.sw].value;
        numLiveNeighbors += liveLookup[neighbors.s].value;
        numLiveNeighbors += liveLookup[neighbors.se].value;

        //Note math.Select(falseValue, trueValue, boolSelector)
        nextState.value = math.select( born[numLiveNeighbors],stay[numLiveNeighbors], live.value== 1);
    }).Run();
    
    return default;
}

"var liveLookup = GetComponentDataFromEntity<Live>(); " accesses entities outside the job so needs to a read only variable. [ReadOnly] is not used on local variable but on fields. So it can't used inside a ForEach() lambda expression. So instead a struct is needed. 

"struct SetLive : IJobForEach<NextState, Live, Neighbors> {" that struct has 

"[ReadOnly]public ComponentDataFromEntity<Live> liveLookup; " the readonly field. Then OnUpdate is replaced with this 

protected override JobHandle OnUpdate(JobHandle inputDeps) {
    // make a native array of live components indexed by entity
    ComponentDataFromEntity<Live> statuses = GetComponentDataFromEntity<Live>();

    SetLive neighborCounterJob = new SetLive() {
        liveLookup = statuses,
    };
    
    JobHandle jobHandle = neighborCounterJob.Schedule(this, inputDeps);

    return jobHandle;
}

which assigns to livelookup in the SetLive struct initialization which is allowed for [ReadOnly] fields. Then schedules and returns a jobHandle like UpdateLiveSystem does. Here is the SetLive struct.

struct SetLive : IJobForEach<NextState, Live, Neighbors> {

    [ReadOnly]public ComponentDataFromEntity<Live> liveLookup;

    public void Execute(ref NextState nextState, [ReadOnly] ref Live live,[ReadOnly] refNeighbors neighbors){

        int numLiveNeighbors = 0;

        numLiveNeighbors += liveLookup[neighbors.nw].value;
        numLiveNeighbors += liveLookup[neighbors.n].value;
        numLiveNeighbors += liveLookup[neighbors.ne].value;
        numLiveNeighbors += liveLookup[neighbors.w].value;
        numLiveNeighbors += liveLookup[neighbors.e].value;
        numLiveNeighbors += liveLookup[neighbors.sw].value;
        numLiveNeighbors += liveLookup[neighbors.s].value;
        numLiveNeighbors += liveLookup[neighbors.se].value;

        //Note math.Select(falseValue, trueValue, boolSelector)
        // did not want to pass in arrays so change to
        // 3 selects
        int bornValue = math.select(0, 1, numLiveNeighbors == 3);
        int stayValue = math.select(0, 1, numLiveNeighbors == 2);
        stayValue = math.select(stayValue, 1, numLiveNeighbors == 3);

        nextState.value = math.select( bornValue,stayValue, live.value== 1);
    }
}

The Execute function has different format for its signature than lambda did before. 

"(ref NextState nextState, in Live live, in Neighbors neighbors)" changed to 

"(ref NextState nextState, [ReadOnly] ref Live live,[ReadOnly] ref Neighbors neighbors)"

"in" is just short hand for [ReadOnly] ref so this signatures are really the same but compiler does not recognize this case yet.The other thing that has been changed is 

nextState.value = math.select( born[numLiveNeighbors],stay[numLiveNeighbors], live.value== 1);

changed to 

int bornValue = math.select(0, 1, numLiveNeighbors == 3);
int stayValue = math.select(0, 1, numLiveNeighbors == 2);
stayValue = math.select(stayValue, 1, numLiveNeighbors == 3);
nextState.value = math.select( bornValue,stayValue, live.value== 1);

job structures cannot have variable length fields in them which is why ComponentDataFromEntity<Live> is a pointer to an array not the whole array. Instead of setting up born and stay as pointers to arrays I just switched 2 array lookups to 3 select statements I don't know which is faster so I did the one that faster for me to write. 

After all this the ECS multithreaded code only runs a few percent faster than the GameObject code on a 400x400 grid :( . Why? It turns out that this code is bound by setting up the graphics for 160,000 cells. I used the Hybrid graphics system which still fairly young. I could not find a way to unenable the renderer of the cells in ECS as I did in the GameObject version. So I just moved them out of the Camera view which still means some graphics code is still calculated then result not used. I think the future version of DOTS will add features to handle just turning off a renderer. The other reason the ECS is not faster is that the simulation is very simple and fast. So even though it runs much faster on the ECS it does not make much difference. Something like simulating a complete city with traffic in Cities Skylines is an example of a case where faster simulation would have an effect.

Let's block ads! (Why?)



"tutorial" - Google News
January 03, 2020 at 09:26AM
https://ift.tt/36wIrOY

Unity DOTS Life Tutorial entities 0.4.0 preview.10 #2 Worker Threads - GameDev.net
"tutorial" - Google News
https://ift.tt/2N1vmVJ
Shoes Man Tutorial
Pos News Update
Meme Update
Korean Entertainment News
Japan News Update

No comments:

Post a Comment