Search Unity

  1. Unity 2019.2 is now released.
    Dismiss Notice

Anyone got Jobs and AsyncGPUReadback to coexist on a coroutine?

Discussion in 'Data Oriented Technology Stack' started by laurentlavigne, Jan 19, 2018.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,030
    In a coroutine
    Job work
    AsyncGPUReadback works
    But when combined in a coroutine, separated by yield return WaitWhile()... oh la la. I've had a variety of symptoms from nullrefs on things that were clearly assigned, to hard crash.
    So I was wondering if anyone got more luck with these and how they did it, pattern etc...
     
  2. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    2,030
    For the record here is how I do it
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using Unity.Collections;
    5. using UnityEngine.Experimental.Rendering;
    6. using Unity.Jobs;
    7. using UnityEngine.Jobs;
    8.  
    9. public class InfluenceCompute : Simulation {
    10.     public ComputeShader shader;
    11.     RenderTexture output;
    12.     [SerializeField] float cellSize=1;
    13.     ComputeBuffer buffer;
    14.     [SerializeField] Material materialViz;
    15.  
    16.     public DiffusionSettings settings;
    17.  
    18.     Vector2Int size;
    19.  
    20.     /*
    21.     struct EmitterToPosition : IJobParallelForTransform
    22.     {
    23.         [WriteOnly] public NativeArray<int> output;
    24.  
    25.         [ReadOnly] public NativeArray<float> emitterValues;
    26.         [ReadOnly] public Vector2Int size;
    27.         [ReadOnly] public Bounds gridBounds;
    28.  
    29.         public void Execute(int i, TransformAccess transform)
    30.         {
    31.             var pos = World2Grid(transform.position);
    32.             output[i] = pos.x + pos.y * size.x;
    33.         }
    34.  
    35.         public Vector3Int World2Grid(Vector3 pos)
    36.         {
    37.             var v = new Vector2(gridBounds.max.x - pos.x, -pos.z + gridBounds.max.z) * size / gridBounds.size.z;
    38.             v.x = Mathf.Clamp(v.x, 0, size.x);
    39.             v.y = Mathf.Clamp(v.y, 0, size.y);
    40.             return new Vector3Int((int)v.x, (int)v.y, 0);
    41.         }
    42.     }
    43.     struct FillDataObstacles : IJobParallelForTransform
    44.     {
    45.         public ComputeBuffer obstaclesBuffer;
    46.         public DiffusionSettings settings;
    47.         public Bounds gridBounds;
    48.         public Vector2Int size;
    49.  
    50.         public void Execute(int index, TransformAccess transform)
    51.         {
    52.             var obstalceValues = new bool[size.x * size.y];
    53.             foreach (var e in obstacles)
    54.             {
    55.                 var bounds = World2Grid(e.GetBounds());
    56.                 var bX = new Vector2Int(bounds.min.x, bounds.max.x);
    57.                 var bY = new Vector2Int(bounds.min.y, bounds.max.y);
    58.                 for (int x = bX.x; x < bX.y; x++)
    59.                 {
    60.                     for (int y = bY.x; y < bY.y; y++)
    61.                     {
    62.                         obstalceValues[x + y * size.x] = true;
    63.                     }
    64.                 }
    65.             }
    66.             obstaclesBuffer.SetData(obstalceValues);
    67.         }
    68.  
    69.         public Vector3Int World2Grid(Vector3 pos)
    70.         {
    71.             var v = new Vector2(gridBounds.max.x - pos.x, -pos.z + gridBounds.max.z) * size / gridBounds.size.z;
    72.             v.x = Mathf.Clamp(v.x, 0, size.x);
    73.             v.y = Mathf.Clamp(v.y, 0, size.y);
    74.             return new Vector3Int((int)v.x, (int)v.y, 0);
    75.         }
    76.         public BoundsInt World2Grid(Bounds bounds)
    77.         {
    78.             var b = new BoundsInt(World2Grid(bounds.center), World2Grid(bounds.size));
    79.             return b;
    80.         }
    81.     }
    82.     */
    83.  
    84.     public override void OnEnable()
    85.     {
    86.         base.OnEnable();
    87.         size = new Vector2Int((int)(gridBounds.size.x/cellSize), (int)(gridBounds.size.z/cellSize));
    88.         buffer = new ComputeBuffer(size.x * size.y, 4 * sizeof(float));
    89.  
    90.         output = new RenderTexture(size.x, size.y, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear);
    91.         output.filterMode = FilterMode.Point;
    92.         output.enableRandomWrite = true;
    93.         output.Create();
    94.         materialViz.SetTexture("_MainTex", output);
    95.  
    96.  
    97.         StartCoroutine(AsyncExtract());
    98.     }
    99.  
    100.     IEnumerator AsyncExtract()
    101.     {
    102.         while (true)
    103.         {
    104.             // -- main thread
    105.             var emitterValues = new float[size.x * size.y];
    106.             foreach (var e in settings.emitters)
    107.             {
    108.                 var pos = World2Grid(e.transform.position);
    109.                 emitterValues [pos.x + pos.y * size.x] = e.value;
    110.             }
    111.             ComputeBuffer emittersBuffer = new ComputeBuffer(size.x * size.y, sizeof(float));
    112.             emittersBuffer.SetData(emitterValues);
    113.  
    114.             var obstalceValues = new bool [size.x * size.y];
    115.             foreach (var e in obstacles)
    116.             {
    117.                 var bounds = World2Grid(e.GetBounds());
    118.                 var bX = new Vector2Int(bounds.min.x, bounds.max.x);
    119.                 var bY = new Vector2Int(bounds.min.y, bounds.max.y);
    120.                 for (int x = bX.x; x < bX.y; x++)
    121.                 {
    122.                     for (int y = bY.x; y < bY.y; y++)
    123.                     {
    124.                         obstalceValues[x + y * size.x] = true;
    125.                     }
    126.                 }
    127.             }
    128.             ComputeBuffer obstaclesBuffer = new ComputeBuffer(size.x * size.y, sizeof(bool));
    129.             obstaclesBuffer.SetData(obstalceValues);
    130.             //---
    131.  
    132.             // --- JOBS
    133.             /*
    134.             NativeArray<float> emitterValues = new NativeArray<float>(settings.emitters.Count, Allocator.TempJob);
    135.             var t = new Transform[settings.emitters.Count];
    136.             for (var i= 0;i< settings.emitters.Count;i++)
    137.             {
    138.                 emitterValues[i] = settings.emitters[i].value;
    139.                 t[i]= settings.emitters[i].transform;
    140.             }
    141.             TransformAccessArray taa = new TransformAccessArray(t);
    142.             NativeArray<int> outputIndexes = new NativeArray<int>(settings.emitters.Count, Allocator.TempJob);
    143.             var jobEmitters = new EmitterToPosition
    144.             {
    145.                 emitterValues=emitterValues,
    146.                 output = outputIndexes,
    147.                 size = size,
    148.                 gridBounds = gridBounds
    149.             };
    150.             var handleEmitters = jobEmitters.Schedule(taa);
    151.             yield return new WaitWhile(() => handleEmitters.IsCompleted);
    152.             handleEmitters.Complete();
    153.             // now we make the array
    154.             var e = new float[size.x * size.y];
    155.             for (var i =0; i < outputIndexes.Length; i++)
    156.                 e[outputIndexes[i]] = emitterValues[i];
    157.  
    158.             emittersBuffer
    159.                 .SetData(e);
    160.             taa.Dispose();
    161.             emitterValues.Dispose();
    162.             outputIndexes.Dispose();
    163.             */
    164.             //--- job
    165.  
    166.  
    167.            
    168.             shader.SetBuffer(0, "emitters", emittersBuffer);
    169.             shader.SetBuffer(0, "obstacles", obstaclesBuffer);
    170.             shader.SetTexture(0, "tex", output);
    171.             shader.SetFloat("trail", settings.trail);
    172.             shader.SetFloat("atten", settings.attenuation);
    173.             shader.SetFloat("diffusion", settings.diffusion);
    174.             shader.SetInt("someValue", (int)Time.time);
    175.             shader.Dispatch(0, size.x/ 8, size.y/ 8, 1);
    176.  
    177.             // extract
    178.             AsyncGPUReadbackRequest request = new AsyncGPUReadbackRequest();
    179.             request = AsyncGPUReadback.Request(output);
    180.  
    181.             yield return new WaitUntil(() => request.done);
    182.  
    183.             // do stuff with that data
    184.  
    185.             yield return null;
    186.             buffer.Dispose();
    187.             emittersBuffer.Dispose();
    188.             obstaclesBuffer.Dispose();
    189.            
    190.         }
    191.     }
    192.  
    193.     /*
    194.     private void OnGUI()
    195.     {
    196.         int w = Screen.width / 2;
    197.         int h = Screen.height / 2;
    198.         int s = 512;
    199.  
    200.         GUI.DrawTexture(new Rect(w - s / 2, h - s / 2, s, s), output);
    201.     }
    202.     */
    203.  
    204.     void OnDestroy()
    205.     {
    206.         output.Release();
    207.         buffer.Dispose();
    208.     }
    209.  
    210.     //TODO: send a signal if the thing is out of bounds.
    211.     public Vector3Int World2Grid(Vector3 pos)
    212.     {
    213.         var v = new Vector2(gridBounds.max.x - pos.x, -pos.z + gridBounds.max.z) * size / gridBounds.size.z;
    214.         v.x = Mathf.Clamp(v.x, 0, size.x);
    215.         v.y = Mathf.Clamp(v.y, 0, size.y);
    216.         return new Vector3Int((int)v.x, (int) v.y,0);
    217.     }
    218.  
    219.     public BoundsInt World2Grid(Bounds bounds)
    220.     {
    221.         var b = new BoundsInt(World2Grid(bounds.center), World2Grid(bounds.size));
    222.         return b;
    223.     }
    224.  
    225. }