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

Question Raycast Command Usage

Discussion in 'Entity Component System' started by Ghat-Smith, Nov 9, 2020.

  1. Ghat-Smith

    Ghat-Smith

    Joined:
    Aug 16, 2016
    Posts:
    48
    Hi. I have a Monobehaviour script using several raycasts, and wanted to optimize the process using RaycastCommand. I don't need to get the results instantly, so instead of completing the job, I would like to check during the following frames if the job is completed. But I get an InvalidOperationException when I read the raycast hits results, even if jobHandle.IsCompleted is true.

    Code (CSharp):
    1.     public class RaycastCommandTest : MonoBehaviour
    2.     {
    3.         NativeArray<RaycastCommand> commands;
    4.         NativeArray<RaycastHit> results;
    5.         JobHandle jobHandle = default;
    6.  
    7.         void Update()
    8.         {
    9.             if (jobHandle.IsCompleted)
    10.             {
    11.                 if (results.IsCreated)
    12.                 {
    13.                     bool hasHit = false;
    14.                     for (int i = 0; i < results.Length; i++)
    15.                     {
    16.                         if (results[i].collider != null)
    17.                         {
    18.                             hasHit = true;
    19.                             break;
    20.                         }
    21.                     }
    22.                     Debug.Log("Has Hit : " + hasHit);
    23.  
    24.                     commands.Dispose();
    25.                     results.Dispose();
    26.                 }
    27.  
    28.                 commands = new NativeArray<RaycastCommand>(10, Allocator.TempJob);
    29.                 results = new NativeArray<RaycastHit>(10, Allocator.TempJob);
    30.  
    31.                 for (int i = 0; i < 10; i++)
    32.                 {
    33.                     commands[i] = new RaycastCommand(new Vector3(i, i, i), Vector3.one);
    34.                 }
    35.  
    36.                 jobHandle = RaycastCommand.ScheduleBatch(commands, results, 1);
    37.                 Debug.Log("Raycast Command Schedule Batch");
    38.             }
    39.             else Debug.Log("Waiting Job Completion");
    40.         }
    41.     }

    Exception :
    Code (CSharp):
    1. InvalidOperationException: The previously scheduled job BatchQueryJob`2 writes to the Unity.Collections.NativeArray`1[UnityEngine.RaycastHit] BatchQueryJob`2.results. You must call JobHandle.Complete() on the job BatchQueryJob`2, before you can read from the Unity.Collections.NativeArray`1[UnityEngine.RaycastHit] safely.
    2. Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle.CheckReadAndThrowNoEarlyOut (Unity.Collections.LowLevel.Unsafe.AtomicSafetyHandle handle) (at <2e6610ed29844f85a37924f81509fe5d>:0)
    3. Unity.Collections.NativeArray`1[T].CheckElementReadAccess (System.Int32 index) (at <2e6610ed29844f85a37924f81509fe5d>:0)
    4. Unity.Collections.NativeArray`1[T].get_Item (System.Int32 index) (at <2e6610ed29844f85a37924f81509fe5d>:0)
    5. RaycastCommandTest.Update () (at Assets/Prototypes/SteeringBehaviour/Scripts/RaycastCommandTest.cs:23)
    Why do I have to call JobHandle.Complete() if the JobHandle is already marked as completed ?
    Could it be a bug where JobHandle is not completed but IsCompleted returns true ?

    Note : I tried to call JobHandle.ScheduleBatchedJobs() right after RaycastCommand.ScheduleBatch but it don't change anything.


    Also, I would be interested to know the best way to make a huge number of raycasts while being able to stop directly when I hit something. So if I have, let's say, 1 000 raycasts to check, but the first one already hit something, I don't want to process all the remaining raycasts. Of course, I can do it with a loop of Physics.Raycast, but can't see how to implement something like that with RaycastCommand. Is there any other way to make raycast directly from my own job ?
    My project don't use ECS, only GameObjects and Jobs/Burst. I saw the Unity Physics package has methods to make collision queries. Is it possible (and a good idea) to use this package, only to make collision queries, and in a Gameobjects project ?
     
  2. Antypodish

    Antypodish

    Joined:
    Apr 29, 2014
    Posts:
    10,574
    If you want stop early, you need do it single threaded.

    Question is, whether is worth it, if you can run them in parallel and with burst.
    How often you run these raycasts?
     
    Ghat-Smith likes this.
  3. Ghat-Smith

    Ghat-Smith

    Joined:
    Aug 16, 2016
    Posts:
    48
    Yes, that's why I want to profile both solutions. But ideally, I would like the RaycastCommand job to be completed in the background (over several frames if needed), instead of having to complete it in one frame.

    Raycasts are for my AI vision system, the frequency and the number of raycasts are defined by several settings (as well as the number of AI agents). Of course these settings will be defined to avoid fps drop etc, but optimizing the costly raycast operations means we can improve AI precision for example (or have more ms for something else).
     
  4. eizenhorn

    eizenhorn

    Joined:
    Oct 17, 2016
    Posts:
    2,653
    Complete() should always be called even if IsCompleted true if you want to process results. It's by design. In SystemBase (Entities package) in most cases BeforeOnUpdate doing this for you in regular dependency chains behind the scene, but any job chains out of dependency flow should be Complete()'d manually (like in your case where you manually process your job dependencies).
     
    Ghat-Smith likes this.
  5. Ghat-Smith

    Ghat-Smith

    Joined:
    Aug 16, 2016
    Posts:
    48
    Right, I didn't know that. Having to call a method named Complete right after ensuring IsCompleted is true seems a bit counter intuitive in my case. I would be curious to know what the method is doing when IsCompleted is already true. I guess it sets flags related to safety checks and ensures dependencies are completed.

    Anyway, thank you guys for your replies.

    After profiling and several tests, here is what i ended up with :
    1. Schedule a job which is preparing the raycast commands
    2. Let the job complete in the background
    3. ScheduleBatch the RaycastCommand job and complete it (required to keep synchronization with the physic world). Then schedule a new job to process hits results and let it work in the background
    4. Get back processed results from the last job and do logic which could not be done in the job (because involving managed references).

    The task was not as easy as expected, I encountered several crashes.
    1. If RaycastCommand is not completed directly (especially if new colliders are instantiated ?)
    2. If I pass my first job as a dependency of the RaycastCommand
    Both have been reported like years ago...
    3. If RaycastCommmand job has RaycastCommand array with some elements not initialized

    I'm still wondering if using Unity Physics package collisions queries would be possible and worth it.
     
    Last edited: Nov 10, 2020
    CPlusSharp22 likes this.
  6. snacktime

    snacktime

    Joined:
    Apr 15, 2013
    Posts:
    3,356
    Using Unity.Physics for queries is going to be far better longer term most likely. We migrated over months of time starting with queries. I couldn't have been happier to finally ditch RaycastCommand which was an absolute pain in my side. Spent hours debugging crashes and other bugs. RaycastCommand will never get proper support with Unity.Physics being a thing now.