Search Unity

A more efficient Job that controls all particles in many ParticleSystems.

Discussion in 'Scripting' started by WakeupFive, Feb 12, 2019.

  1. WakeupFive

    WakeupFive

    Joined:
    Nov 1, 2016
    Posts:
    5
    I have implemented the ability to control the height and color of many particles.

    I have implemented the necessary functions using the Job system, but there is a time-consuming problem in reading and writing particles.

    Is there anything I can do to improve my code?

    There are 1000 ParticleSystems and each with 50 particles, controlling a total of 50,000 particles.
    Version is 2018.2.20f1.

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using Unity.Collections;
    4. using Unity.Jobs;
    5. using UnityEngine;
    6.  
    7. public class LEDController : MonoBehaviour
    8. {
    9.     [SerializeField]
    10.     private ParticleSystem[] particles;
    11.  
    12.     private ParticleSystem.Particle[][] all;
    13.     private int[] lengths;
    14.  
    15.     private void Start()
    16.     {
    17.         all = new ParticleSystem.Particle[particles.Length][];
    18.  
    19.         for (int i = 0; i < all.Length; i++)
    20.         {
    21.             all[i] = new ParticleSystem.Particle[particles[i].main.maxParticles];
    22.         }
    23.  
    24.         lengths = new int[particles.Length];
    25.     }
    26.  
    27.     private void Update()
    28.     {
    29.         // Total time : 22ms
    30.         //
    31.         int psLength = all.Length;
    32.         float time = (Time.time % 0.25f) / 0.25f;
    33.         int allCount = 0;
    34.  
    35.         // get particles
    36.         // time : 2.5ms
    37.         for (int i = 0; i < psLength; i++)
    38.         {
    39.             int length = particles[i].GetParticles(all[i]);
    40.             allCount += length;
    41.             lengths[i] = length;
    42.         }
    43.  
    44.         var info = new NativeArray<Info>(allCount, Allocator.Temp);
    45.      
    46.         // get particles info
    47.         // time : 5.5ms
    48.         int count = 0;
    49.         for (int i = 0; i < psLength; i++)
    50.         {
    51.             for (int j = 0; j < lengths[i]; j++)
    52.             {
    53.                 var infoA = new Info();
    54.                 infoA.pos = all[i][j].position;// = new Info(all[i][j]);
    55.                 infoA.color = all[i][j].startColor;// = new Info(all[i][j]);
    56.  
    57.                 info[count] = infoA;
    58.  
    59.                 count++;
    60.             }
    61.         }
    62.  
    63.         // execute job
    64.         // time : 3ms
    65.         VerticalJob job = new VerticalJob();
    66.  
    67.         job.info = info;
    68.         job.time = time;
    69.  
    70.         var handle = job.Schedule(allCount, 1);
    71.         handle.Complete();
    72.  
    73.         // set particles info
    74.         // time : 7.5ms
    75.         count = 0;
    76.         for (int i = 0; i < psLength; i++)
    77.         {
    78.             for (int j = 0; j < lengths[i]; j++)
    79.             {
    80.                 all[i][j].position = info[count].pos;
    81.                 all[i][j].startColor = info[count].color;
    82.  
    83.                 count++;
    84.             }
    85.         }
    86.  
    87.         // set particles
    88.         // time : 3.5ms
    89.         for (int i = 0; i < psLength; i++)
    90.         {
    91.             particles[i].SetParticles(all[i], lengths[i]);
    92.         }
    93.  
    94.         // End
    95.         info.Dispose();
    96.     }
    97.  
    98.     public struct VerticalJob : IJobParallelFor
    99.     {
    100.         [ReadOnly]
    101.         public float time;
    102.  
    103.         public NativeArray<Info> info;
    104.  
    105.         public void Execute(int index)
    106.         {
    107.             Info i = info[index];
    108.  
    109.             Vector3 p = i.pos;
    110.             float alpha = (p.x + time) % 100f;
    111.  
    112.             Color32 c = i.color;
    113.             c.a = (byte)(255 * (alpha / 100f));
    114.             i.color = c;
    115.  
    116.             p.y = alpha;
    117.             i.pos = p;
    118.  
    119.             info[index] = i;
    120.         }
    121.     }
    122.  
    123.     public struct Info
    124.     {
    125.         public Vector3 pos;
    126.         public Color color;
    127.     }
    128. }
     
  2. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    You can postpone .Complete on the job, simply by storing a handle and calling complete in .LateUpdate(). This should give the job some time in background without hanging main thread.

    Also, note that there's an actual ParticleJob available exactly for that, and should elliminate .Get/.Set on the particle system and scheduling / completing.

    https://forum.unity.com/threads/release-particle-system-c-job-system-support.529284/

    Example from that link:
    Code (CSharp):
    1. using Unity.Collections;
    2. using UnityEngine;
    3. using UnityEngine.Experimental.ParticleSystemJobs;
    4. public class ParticleJob : MonoBehaviour
    5. {
    6.     void Start ()
    7.     {
    8.         var job = new UpdateParticlesJob();
    9.         job.color = Color.red;
    10.         job.size = 0.35f;
    11.         GetComponent<ParticleSystem>().SetJob(job);
    12.     }
    13.     struct UpdateParticlesJob : IParticleSystemJob
    14.     {
    15.         [ReadOnly]
    16.         public Color color;
    17.         [ReadOnly]
    18.         public float size;
    19.         public void ProcessParticleSystem(JobData particles)
    20.         {
    21.             var startColors = particles.startColors;
    22.             var sizes = particles.sizes.x;
    23.             for (int i = 0; i < particles.count; i++)
    24.             {
    25.                 startColors[i] = color;
    26.                 sizes[i] = size;
    27.             }
    28.         }
    29.     }
    30. }
     
  3. WakeupFive

    WakeupFive

    Joined:
    Nov 1, 2016
    Posts:
    5
    Thanks, but I moved "JobHandle.Complate ()" to "LateUpdate", but there was little difference in performance ...

    And when IParticleSystemJob is not found in the 2018.3 version, when is it available?
     
  4. xVergilx

    xVergilx

    Joined:
    Dec 22, 2014
    Posts:
    3,296
    If it's not a part of Jobs package (maybe an upgrade?) then I'd say it's 2019 feature.
     
  5. jjxtra

    jjxtra

    Joined:
    Aug 30, 2013
    Posts:
    1,464
    Is it possible to do custom sort on particles using a job? The Unity distance sort is not sufficient for mesh based particles, as you move through the particles or rotate the camera particles pop in and out in front of each other. The sort needs to be on closest box or sphere surface instead of center, I am writing this but finding it slow to sort because I am using get/set particle calls.