Search Unity

Question How do you access an AsyncGPUReadbackRequest from a job? (unsafe pointer issues)

Discussion in 'C# Job System' started by laurentlavigne, Apr 28, 2023.

  1. laurentlavigne

    laurentlavigne

    Joined:
    Aug 16, 2012
    Posts:
    6,327
    I am trying to go through 5M pixels of a 3D texture in a job. The 3D texture is held in a compute buffer.
    This code isn't allowed by the safety system and causes problems.
    I could loop through each z in the main thread and pushing a NativeArray<ushort> to the job but this is slow. Is there another way?

    Code (CSharp):
    1. using com.zibra.liquid.Solver;
    2. using Drawing;
    3. using Fraktalia.VoxelGen;
    4. using Unity.Burst;
    5. using Unity.Collections;
    6. using Unity.Collections.LowLevel.Unsafe;
    7. using Unity.Jobs;
    8. using UnityEngine;
    9. using UnityEngine.Rendering;
    10.  
    11. public class ZibraLiquidGridReadback : MonoBehaviour
    12. {
    13.     public                              ZibraLiquid             liquid;
    14.     public                              VoxelGenerator          voxel;
    15.     [NativeDisableUnsafePtrRestriction] AsyncGPUReadbackRequest newRequest, oldRequest;
    16.     bool                                                        initialized;
    17.     public bool                                                 drawGizmos;
    18.     public float                                                threshold = 0.01f;
    19.     public int                                                  step      = 2;
    20.  
    21.     //capture liquid's density textyre
    22.     void Update()
    23.     {
    24.         if (liquid.DensityTexture == null) return;
    25.         if (!initialized)
    26.         {
    27.             newRequest  = AsyncGPUReadback.Request(liquid.DensityTexture);
    28.             initialized = true;
    29.         }
    30.         newRequest.Update();
    31.         var gridSize                        = liquid.GridSize;
    32.         if (newRequest.hasError) newRequest = AsyncGPUReadback.Request(liquid.DensityTexture);
    33.         if (newRequest.done)
    34.         {
    35.             oldRequest = newRequest;
    36.             newRequest = AsyncGPUReadback.Request(liquid.DensityTexture);
    37.         }
    38.         JobLauncher();
    39.     }
    40.  
    41.     [BurstCompile]
    42.     public struct DrawJob : IJobParallelFor
    43.     {
    44.         [ReadOnly] public Vector3    edge;
    45.         [ReadOnly] public Vector3Int gridSize;
    46.         [ReadOnly] public int        step;
    47.         [ReadOnly] public float      threshold;
    48.         [ReadOnly] public float      nodeSize;
    49.         [ReadOnly] [NativeDisableUnsafePtrRestriction]
    50.         public AsyncGPUReadbackRequest oldRequest;
    51.         public CommandBuilder builder;
    52.  
    53.         public void Execute(int z)
    54.         {
    55.             var data = oldRequest.GetData<ushort>(z);
    56.             using (builder.WithDuration(.2f))
    57.             {
    58.                 for (var x = 0; x < gridSize.x; x += step)
    59.                 {
    60.                     for (var y = 0; y < gridSize.y; y += step) ExtractDensities(z, x, y, data);
    61.                 }
    62.             }
    63.         }
    64.  
    65.         void ExtractDensities(int z, int x, int y, NativeArray<ushort> data)
    66.         {
    67.             var flatIndex = 4 * (x + y * gridSize.x);
    68.             var density   = Mathf.HalfToFloat(data[flatIndex + 3]);
    69.             if (density > threshold)
    70.             {
    71.                 var material1 = Mathf.HalfToFloat(data[flatIndex]);
    72.                 var material2 = Mathf.HalfToFloat(data[flatIndex + 1]);
    73.                 var material3 = Mathf.HalfToFloat(data[flatIndex + 2]);
    74.                 var material0 = density - material1 - material2 - material3;
    75.                 var color     = new Color(material1, material2, material3);
    76.                 builder.WireBox(edge + nodeSize * new Vector3(x, y, z), nodeSize * new Vector3(1f, 1f, 1f) * step, color);
    77.             }
    78.         }
    79.     }
    80.  
    81.     void JobLauncher()
    82.     {
    83.         if (!drawGizmos || !initialized || oldRequest.hasError || !oldRequest.done) return;
    84.         var edge     = transform.position - liquid.ContainerSize / 2.0f;
    85.         var gridSize = liquid.GridSize;
    86.         var builder  = DrawingManager.GetBuilder(true);
    87.         builder.Preallocate(gridSize.x * gridSize.y * gridSize.z * 4);
    88.         var drawJob = new DrawJob
    89.         {
    90.             oldRequest = oldRequest, edge = edge, gridSize = gridSize, step = step, threshold = threshold, nodeSize = liquid.NodeSize,
    91.             builder    = builder
    92.         };
    93.         var jobHandle = drawJob.Schedule(gridSize.z / step, 8);
    94.         jobHandle.Complete();
    95.         builder.Dispose();
    96.     }
    97. }