Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. Dismiss Notice

Using strings in NativeArrays and IJobParallelFor jobs?

Discussion in 'Entity Component System' started by DehLeprechaun, Nov 14, 2020.

  1. DehLeprechaun

    DehLeprechaun

    Joined:
    Dec 2, 2015
    Posts:
    5
    Howdy,

    I'm just dipping my toes into the new DOTS workflow, and I'm playing with the job system. I'm exploring the concepts that make open world games possible, and I'm working on loading and unloading the world around the player using additively loaded scenes.

    Each scene currently holds just a Terrain object, and has a unique name to indicate its location on the overall map. I use a prefab empty gameobject with the same name in the world to check the player's location against, and then if the player is close enough to that gameobject it will load the scene associated with it. A normal ForEach loop, with enough scenes, will take a long time to check the distance to all the other scenes. A solution I wanted to explore to reduce that time was the Job system.

    Unfortunately, it seems like I can't use the SceneManager.LoadScene() function inside of a IJobParallelFor job because the NativeArray doesn't accept strings. I might be able to work around this using the build index, but it brings up the question: Is there a way to get strings into a NativeArray if you needed to work with string objects specifically?

    Here's my attempt at creating a job struct:
    Code (CSharp):
    1. public struct LoadTerrainJob : IJobParallelFor
    2. {
    3.     public NativeArray<string> SceneNames;
    4.     public NativeArray<float3> playerLocs;
    5.     public NativeArray<float3> sceneLoc;
    6.     public NativeArray<float> rads;
    7.  
    8.     public void Execute(int index)
    9.     {
    10.        
    11.     }
    12. }
    The NativeArray<string> throws the following error: The type 'string' must be a non-nullable value type in order to use it as parameter 'T' in the generic type or method 'NativeArray<T>'.
     
    DragonCoder likes this.
  2. nobeerleft

    nobeerleft

    Joined:
    Mar 29, 2012
    Posts:
    27
    Try the FixedString* types. They might work for you
     
  3. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,574
    Think about solutions, where you don't need strings in jobs. Is just wasteful.
    Use unique IDs if have to.
    You can even use entities as your identifier.

    Regarding calling scene, you can do in many ways. You can create an entity in job and in other system in main thread, use that entity with scene ID, to open scene you need.

    But generally, I think you doing something wrong, if you need iterate through many scenes.
    Consider using spatial mapping, to find nearest points (scenes / objects / etc) of interest.
     
  4. DehLeprechaun

    DehLeprechaun

    Joined:
    Dec 2, 2015
    Posts:
    5
    Well, to update on my attempt to load scenes using the job system: It seems either that I've done something wrong, or that using the Scenemanager through the job system takes longer than using coroutines and async loading. I suspect it's the former. I'll post my code, but I don't expect anyone to help me fix the job:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4. using UnityEngine.SceneManagement;
    5. using UnityEditor.SceneManagement;
    6. using Unity.Jobs;
    7. using Unity.Mathematics;
    8. using Unity.Collections;
    9.  
    10. public struct LoadTerrainJob : IJobParallelFor
    11. {
    12.     public NativeArray<TerrainSceneLoader.SceneData> SceneDatas;
    13.  
    14.     public void Execute(int index)
    15.     {
    16.         var data = SceneDatas[index];
    17.         data.Update();
    18.         SceneDatas[index] = data;
    19.     }
    20. }
    21.  
    22. [ExecuteInEditMode]
    23. public class TerrainSceneLoader : MonoBehaviour
    24. {
    25.     public bool useJobs = false;
    26.     public Transform PlayerLocation;
    27.     public float LoadRadius = 500f;
    28.     public List<GameObject> CellPrefabs;    //CellPrefabs have the scene name and location to spawn
    29.  
    30.     public struct SceneData
    31.     {
    32.         public int SceneBuildIndex;
    33.         public float3 SceneLoc;
    34.         public float3 PlayerLoc;
    35.         public float Radius;
    36.  
    37.         public void Update()
    38.         {
    39.             if (Application.isPlaying)
    40.             {
    41.                 float dist = math.distance(PlayerLoc, SceneLoc);
    42.                 if (dist < Radius && !SceneManager.GetSceneByBuildIndex(SceneBuildIndex).isLoaded)
    43.                 {
    44.                     SceneManager.LoadScene(SceneBuildIndex, LoadSceneMode.Additive);
    45.                 }
    46.                 else if (SceneManager.GetSceneByBuildIndex(SceneBuildIndex).isLoaded)
    47.                 {
    48.                     SceneManager.UnloadSceneAsync(SceneBuildIndex);
    49.                 }
    50.             }
    51.         }
    52.  
    53.         public SceneData(int SBI, GameObject CellPrefab, float3 PlayerLocation, float LoadRadius)
    54.         {
    55.             SceneBuildIndex = SBI;
    56.             SceneLoc = CellPrefab.transform.position;
    57.             PlayerLoc = PlayerLocation;
    58.             Radius = LoadRadius;
    59.         }
    60.     }
    61.  
    62.     private void Start()
    63.     {
    64.         if(PlayerLocation == null)
    65.         {
    66.             PlayerLocation = GameObject.FindGameObjectsWithTag("Player")[0].transform;
    67.         }
    68.     }
    69.  
    70.     private void Update()
    71.     {
    72.         float startTime = Time.realtimeSinceStartup;
    73.         if (useJobs)
    74.         {
    75.             var SceneDataArray = new NativeArray<TerrainSceneLoader.SceneData>(
    76.                 CellPrefabs.Count, Allocator.TempJob);
    77.             for(var i = 0; i < CellPrefabs.Count; i++)
    78.             {
    79.                 SceneDataArray[i] = new TerrainSceneLoader.SceneData(
    80.                     SceneManager.GetSceneByName(CellPrefabs[i].name).buildIndex,
    81.                     CellPrefabs[i], PlayerLocation.position, LoadRadius);
    82.             }
    83.             var job = new LoadTerrainJob
    84.             {
    85.                 SceneDatas = SceneDataArray
    86.             };
    87.             var jobHandle = job.Schedule(CellPrefabs.Count, 1);
    88.             jobHandle.Complete();
    89.             SceneDataArray.Dispose();
    90.         }
    91.         else
    92.         {
    93.             Vector3 pLoc = new Vector3(
    94.             PlayerLocation.position.x,
    95.             0,
    96.             PlayerLocation.position.z);
    97.  
    98.             foreach (GameObject cell in CellPrefabs)
    99.             {
    100.                 if (Vector3.Distance(pLoc,
    101.                     cell.transform.position) < LoadRadius)
    102.                 {
    103.                     StartCoroutine(LoadScene(cell.name));
    104.                 }
    105.                 else if (Vector3.Distance(pLoc,
    106.                    cell.transform.position) > LoadRadius)
    107.                 {
    108.                     StartCoroutine(UnloadScene(cell.name));
    109.                 }
    110.             }
    111.         }
    112.         Debug.Log(((Time.realtimeSinceStartup - startTime) * 1000f) + "ms");
    113.     }
    114.  
    115.     IEnumerator LoadScene(string SceneName)
    116.     {
    117.         if(!SceneManager.GetSceneByName(SceneName).isLoaded && Application.isPlaying)
    118.         {
    119.             AsyncOperation asyncLoad = SceneManager.LoadSceneAsync(SceneName, LoadSceneMode.Additive);
    120.  
    121.             while (!asyncLoad.isDone)
    122.             {
    123.                 yield return null;
    124.             }
    125.         }
    126.         yield return null;
    127.     }
    128.  
    129.     IEnumerator UnloadScene(string SceneName)
    130.     {
    131.         if (SceneManager.GetSceneByName(SceneName).isLoaded && Application.isPlaying)
    132.         {
    133.             AsyncOperation asyncUnload = SceneManager.UnloadSceneAsync(SceneName);
    134.  
    135.             while (!asyncUnload.isDone)
    136.             {
    137.                 yield return null;
    138.             }
    139.         }
    140.         yield return null;
    141.     }
    142.  
    143. }
    144.  
    With the jobs, times range somewhere between 2 and 9 ms. With coroutines and async loading, times range somewhere between 0.1 and 0.3 ms.