Search Unity

Raycasting Is Super Slow

Discussion in 'DOTS Physics' started by inSight01, Dec 18, 2020.

  1. inSight01

    inSight01

    Joined:
    Apr 18, 2017
    Posts:
    14
    I'm playing around with Unity DOTS and am trying to do some raycasting but I'm getting abysmal results. I tried following the examples here and here (My code is identical to the first link). I decided to compare PhysX, Unity.Physics and Unity.Physics with Jobs+Burst and run 10,000 raycasts. My results were.

    PhysX: 6ms
    Unity Physics: 70ms
    Unity Physics (Jobs and Burst): 350ms

    Normally I get pretty great results with Jobs and Burst so I'm not entirely sure what I'm doing wrong. When I look at the Profiler the script running the above is taking up the whole process. When I look at the Entity Debugger I noticed BuildPhysicsWorld and ExportPhysicsWorld is taking up the most time, especially when using Unity Physics with Jobs+Burst.
     
  2. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Does the job in the profiler show up as (Burst)? Could it be that you are measuring the time of the Burst compilation as well? Try making Burst synchronous.

    Also, this might sound silly, but check for any debug/safety checks, jobs debugger and leak detection in the editor.
     
  3. inSight01

    inSight01

    Joined:
    Apr 18, 2017
    Posts:
    14
    Silly me forgot to add [BurstCompile]. Went from 350ms to 150ms (although the first run was 350ms still, perhaps this is due to compilation?). Adding CompileSynchronously = true had no effect on performance.


    No, not that I can see. Where do I look for that?

    Turning them off drops it to ~100ms. Still slower than using Unity.Physics alone and over 10x slower than PhysX.


    Code (CSharp):
    1. public class RaycastTest : MonoBehaviour
    2.     {
    3.         [SerializeField] private int numRaysCast = 10000;
    4.         [SerializeField] private float rayDistance = 1000f;
    5.         [SerializeField] private Method method = Method.PhysX;
    6.  
    7.         private BuildPhysicsWorld buildPhysicsWorld;
    8.         private CollisionWorld collisionWorld;
    9.  
    10.         private System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
    11.      
    12.         private enum Method
    13.         {
    14.             PhysX,
    15.             UnityPhysics,
    16.             UnityPhysicsJobsBurst
    17.         }
    18.  
    19.         private void Start()
    20.         {
    21.             buildPhysicsWorld = World.DefaultGameObjectInjectionWorld.GetExistingSystem<BuildPhysicsWorld>();
    22.             collisionWorld = buildPhysicsWorld.PhysicsWorld.CollisionWorld;
    23.         }
    24.  
    25.         private void Update()
    26.         {
    27.             if (Input.GetMouseButtonDown(0))
    28.             {
    29.                 stopwatch.Reset();
    30.                 stopwatch.Start();
    31.              
    32.                 UnityEngine.Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    33.  
    34.                 UnityEngine.RaycastHit physxRaycastHit;
    35.                 Unity.Physics.RaycastHit unityRaycastHit;
    36.  
    37.                 bool hitDetected = false;
    38.  
    39.                 Unity.Physics.RaycastInput raycastInput = new Unity.Physics.RaycastInput
    40.                 {
    41.                     Start = ray.origin,
    42.                     End = ray.direction * rayDistance,
    43.                     Filter = new CollisionFilter
    44.                     {
    45.                         BelongsTo = ~0u,
    46.                         CollidesWith = ~0u,
    47.                         GroupIndex = 0
    48.                     }
    49.                 };
    50.          
    51.                 for (int i = 0; i < numRaysCast; i++)
    52.                 {
    53.                     switch (method)
    54.                     {
    55.                         case Method.PhysX:
    56.                             hitDetected = UnityEngine.Physics.Raycast(ray.origin, ray.direction * rayDistance, out physxRaycastHit);
    57.                             break;
    58.                         case Method.UnityPhysics:
    59.                             hitDetected = collisionWorld.CastRay(raycastInput, out unityRaycastHit);
    60.                             break;
    61.                         case Method.UnityPhysicsJobsBurst:
    62.                             hitDetected = SingleRayCast(collisionWorld, raycastInput, out unityRaycastHit);
    63.                             break;
    64.                     }
    65.                 }
    66.          
    67.                 stopwatch.Stop();
    68.  
    69.                 if (hitDetected)
    70.                 {
    71.                     Debug.Log(method.ToString() + " took " + stopwatch.ElapsedMilliseconds + "ms to complete.");
    72.                 }
    73.             }
    74.         }
    75.      
    76.         public static bool SingleRayCast(CollisionWorld collisionWorld, RaycastInput raycastInput, out Unity.Physics.RaycastHit raycastHit)
    77.         {
    78.             NativeArray<RaycastInput> raycastInputs = new NativeArray<RaycastInput>(1, Allocator.TempJob);
    79.             NativeArray<Unity.Physics.RaycastHit> raycastHits = new NativeArray<Unity.Physics.RaycastHit>(1, Allocator.TempJob);
    80.  
    81.             raycastInputs[0] = raycastInput;
    82.  
    83.             JobHandle jobHandle = ScheduleBatchRayCast(collisionWorld, raycastInputs, raycastHits);
    84.             jobHandle.Complete();
    85.          
    86.             raycastHit = raycastHits[0];
    87.  
    88.             raycastInputs.Dispose();
    89.             raycastHits.Dispose();
    90.  
    91.             return raycastHit.Entity != Entity.Null;
    92.         }
    93.      
    94.         public static JobHandle ScheduleBatchRayCast(CollisionWorld collisionWorld, NativeArray<RaycastInput> raycastInputs, NativeArray<Unity.Physics.RaycastHit> raycastHits)
    95.         {
    96.             JobHandle jobHandle = new RaycastJob
    97.             {
    98.                 raycastInputs = raycastInputs,
    99.                 raycastHits = raycastHits,
    100.                 collisionWorld = collisionWorld
    101.             }.Schedule(raycastInputs.Length, 4);
    102.          
    103.             return jobHandle;
    104.         }
    105.      
    106.         [BurstCompile(CompileSynchronously = true)]
    107.         public struct RaycastJob : IJobParallelFor
    108.         {
    109.             [ReadOnly] public CollisionWorld collisionWorld;
    110.             [ReadOnly] public NativeArray<RaycastInput> raycastInputs;
    111.          
    112.             public NativeArray<Unity.Physics.RaycastHit> raycastHits;
    113.    
    114.             public void Execute(int index)
    115.             {
    116.                 Unity.Physics.RaycastHit raycastHit;
    117.                 collisionWorld.CastRay(raycastInputs[index], out raycastHit);
    118.                 raycastHits[index] = raycastHit;
    119.             }
    120.         }
    121.     }
     
  4. rz_0lento

    rz_0lento

    Joined:
    Oct 8, 2013
    Posts:
    2,136
    Just to make sure, did you do the comparisons using a build or in editor? There's a ton of overhead in editor - even after disabling safety checks.

    So what I'm trying to say: Do a development build and attach profiler to it to get some meaningful comparison data.
     
  5. inSight01

    inSight01

    Joined:
    Apr 18, 2017
    Posts:
    14
    Editor.


    Build.
     
  6. rz_0lento

    rz_0lento

    Joined:
    Oct 8, 2013
    Posts:
    2,136
    So the difference is quite linear it appears. btw you might get that PhysX raycasting even faster by using RaycastCommand instead (it's a way to jobify physx raycasts). Would be interesting to see the diff against it anyway.
     
  7. inSight01

    inSight01

    Joined:
    Apr 18, 2017
    Posts:
    14
    I'm a fairly novice programmer but I think I may know what's happening. When sending the raycast off to the job I'm requesting results immediately. So the jobs aren't being batched. This, I would imagine, is creating unnecessary overhead hence the reason it's slower than just using Unity Physics by itself.

    If I batch all 10,000 and request the results at a later time I'm sure performance would be drastically improved. I'm still new to jobs so I'll play around with it and see what I can do.

    Still, Unity Physics is over 10 times slower than PhysX when using CastRay(RaycastInput, RaycastHit);. I wonder if this is apparent to Unity Devs.
     
  8. rz_0lento

    rz_0lento

    Joined:
    Oct 8, 2013
    Posts:
    2,136
    What kind of scene are you raycasting against? Can you share the whole project?
     
  9. inSight01

    inSight01

    Joined:
    Apr 18, 2017
    Posts:
    14
    It's literally just two cubes at centre of the scene (One a gameobject for PhysX and one an entity for Unity Physics). I'm then casting a ray from mouse position on the screen using Ray ray = Camera.main.ScreenPointToRay(Input.MousePosition);

    I decided to batch the jobs and the results were amazing. 2ms in Editor and 3ms in Build (Editor was actually faster than Build). So I'm guessing my theory above about the overhead was correct.

    Still, PhysX has amazing performance when it comes to raycasting. Especially given that I'm casting 10,000 rays one at a time whereas with batched Jobs/Burst they are basically being cast all at once. I wonder if it's doing some kind of multi-threaded batching in the background. And if so, I wonder if Unity could implement such a thing without having the user do it themselves.



    The scripts are a total mess at the moment as I was more focused on results than cleanliness. I'll clean it up and post it here.
     
  10. Shane_Michael

    Shane_Michael

    Joined:
    Jul 8, 2013
    Posts:
    129
    I tried recreating your test, and I found that batching all the raycasts into a single job made it ~4.4 times faster than PhysX.

    I also tested using RaycastCommand to batch the PhysX raycasts, and it was on par with Unity Physics but about 10% faster still.

    A scene with a single cube might not be the most useful test, however.
     
  11. inSight01

    inSight01

    Joined:
    Apr 18, 2017
    Posts:
    14
    Create empty GameObject and attach below script. Create two cubes and attached "ConvertToEntity" script to one. Then Play.


    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Collections;
    3. using Unity.Entities;
    4. using Unity.Jobs;
    5. using Unity.Physics;
    6. using Unity.Physics.Systems;
    7. using UnityEngine;
    8.  
    9. public class UnityRaycastTest : MonoBehaviour
    10. {
    11.     [SerializeField] private int numRaysCast = 10000;
    12.  
    13.     private BuildPhysicsWorld buildPhysicsWorld => World.DefaultGameObjectInjectionWorld.GetExistingSystem<BuildPhysicsWorld>();
    14.     private CollisionWorld collisionWorld => buildPhysicsWorld.PhysicsWorld.CollisionWorld;
    15.  
    16.     private System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
    17.  
    18.     private string[] debugMessages = new string[4] {"PhysX", "Unity Physics", "Unity Physics !Batched", "Unity Physics Batched"};
    19.    
    20.     // Update is called once per frame
    21.     private void Update()
    22.     {
    23.         if (Input.GetMouseButtonDown(0))
    24.         {
    25.             UnityEngine.Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    26.  
    27.             // Distance to cast ray
    28.             float rayDistance = 65535f;
    29.            
    30.             UnityEngine.RaycastHit[] physXHits = new UnityEngine.RaycastHit[numRaysCast];
    31.             Unity.Physics.RaycastHit[] unityHits = new Unity.Physics.RaycastHit[numRaysCast];
    32.            
    33.             // Raycast Input for Unity Physics
    34.             Unity.Physics.RaycastInput unityRaycastInput = new RaycastInput
    35.             {
    36.                 Start = ray.origin,
    37.                 End = ray.direction * rayDistance,
    38.                 Filter = new CollisionFilter
    39.                 {
    40.                     BelongsTo = ~0u,
    41.                     CollidesWith = ~0u,
    42.                     GroupIndex = 0
    43.                 }
    44.             };
    45.            
    46.             // Perform PhysX Raycasts
    47.             stopwatch.Reset();
    48.             stopwatch.Start();
    49.  
    50.             for (int i = 0; i < numRaysCast; i++)
    51.             {
    52.                 UnityEngine.Physics.Raycast(ray, out physXHits[i], rayDistance);
    53.             }
    54.            
    55.             stopwatch.Stop();
    56.  
    57.             debugMessages[0] = "PhysX Took " + stopwatch.ElapsedMilliseconds + "ms";
    58.            
    59.             // Perform Unity Physics Raycasts
    60.             stopwatch.Reset();
    61.             stopwatch.Start();
    62.  
    63.             for (int i = 0; i < numRaysCast; i++)
    64.             {
    65.                 collisionWorld.CastRay(unityRaycastInput, out unityHits[i]);
    66.             }
    67.            
    68.             stopwatch.Stop();
    69.  
    70.             debugMessages[1] = "Unity Physics Took " + stopwatch.ElapsedMilliseconds + "ms";
    71.            
    72.             // Perform Unity Physics Raycasts Jobs without Batching
    73.             stopwatch.Reset();
    74.             stopwatch.Start();
    75.  
    76.             for (int i = 0; i < numRaysCast; i++)
    77.             {
    78.                 SingleRayCast(collisionWorld, unityRaycastInput, out unityHits[i]);
    79.             }
    80.            
    81.             stopwatch.Stop();
    82.  
    83.             debugMessages[2] = "Unity Physics Jobs Without Batching Took " + stopwatch.ElapsedMilliseconds + "ms";
    84.            
    85.             // Perform Unity Physics Raycasts Jobs with Batching
    86.             stopwatch.Reset();
    87.             stopwatch.Start();
    88.  
    89.             MultiRayCast(numRaysCast, collisionWorld, unityRaycastInput, out unityHits);
    90.            
    91.             stopwatch.Stop();
    92.  
    93.             debugMessages[3] = "Unity Physics Jobs With Batching Took " + stopwatch.ElapsedMilliseconds + "ms";
    94.         }
    95.     }
    96.    
    97.     private void OnGUI()
    98.     {
    99.         GUIStyle style = new GUIStyle();
    100.         style.fontSize = 20;
    101.         style.fontStyle = FontStyle.Bold;
    102.        
    103.         style.normal.textColor = Color.white;
    104.        
    105.         GUI.Label(new Rect(10, 20, Screen.width - 10, 20), "Rays Cast: " + numRaysCast, style);
    106.         GUI.Label(new Rect(10, 40, Screen.width - 10, 20), debugMessages[0], style);
    107.         GUI.Label(new Rect(10, 60, Screen.width - 10, 20), debugMessages[1], style);
    108.         GUI.Label(new Rect(10, 80, Screen.width - 10, 20), debugMessages[2], style);
    109.         GUI.Label(new Rect(10, 100, Screen.width - 10, 20), debugMessages[3], style);
    110.     }
    111.  
    112.     public static bool SingleRayCast(CollisionWorld collisionWorld, Unity.Physics.RaycastInput raycastInput, out Unity.Physics.RaycastHit raycastHit)
    113.     {
    114.         NativeArray<RaycastInput> raycastInputs = new NativeArray<RaycastInput>(1, Allocator.TempJob);
    115.         NativeArray<Unity.Physics.RaycastHit> raycastHits = new NativeArray<Unity.Physics.RaycastHit>(1, Allocator.TempJob);
    116.  
    117.         raycastInputs[0] = raycastInput;
    118.  
    119.         JobHandle jobHandle = ScheduleBatchRaycast(collisionWorld, raycastInputs, raycastHits);
    120.         jobHandle.Complete();
    121.            
    122.         raycastHit = raycastHits[0];
    123.  
    124.         raycastInputs.Dispose();
    125.         raycastHits.Dispose();
    126.  
    127.         return raycastHit.Entity != Entity.Null;
    128.     }
    129.  
    130.     public static bool MultiRayCast(int numRays, CollisionWorld collisionWorld, Unity.Physics.RaycastInput raycastInput, out Unity.Physics.RaycastHit[] raycastHits)
    131.     {
    132.         NativeArray<RaycastInput> naRaycastInputs = new NativeArray<RaycastInput>(numRays, Allocator.TempJob);
    133.         NativeArray<Unity.Physics.RaycastHit> naRaycastHits = new NativeArray<Unity.Physics.RaycastHit>(numRays, Allocator.TempJob);
    134.  
    135.         raycastHits = new Unity.Physics.RaycastHit[numRays];
    136.  
    137.         bool hitDetected = false;
    138.  
    139.         for (int i = 0; i < numRays; i++)
    140.         {
    141.             naRaycastInputs[i] = raycastInput;
    142.         }
    143.  
    144.         JobHandle jobHandle = ScheduleBatchRaycast(collisionWorld, naRaycastInputs, naRaycastHits);
    145.         jobHandle.Complete();
    146.  
    147.         for (int i = 0; i < numRays; i++)
    148.         {
    149.             raycastHits[i] = naRaycastHits[i];
    150.  
    151.             if (!hitDetected)
    152.             {
    153.                 hitDetected = raycastHits[i].Entity != Entity.Null;
    154.             }
    155.         }
    156.  
    157.         naRaycastInputs.Dispose();
    158.         naRaycastHits.Dispose();
    159.  
    160.         return hitDetected;
    161.     }
    162.    
    163.     // Shcedule the jobs
    164.     public static JobHandle ScheduleBatchRaycast(CollisionWorld collisionWorld, NativeArray<Unity.Physics.RaycastInput> raycastInputs, NativeArray<Unity.Physics.RaycastHit> raycastHits)
    165.     {
    166.         JobHandle jobhandle = new UnityRaycastTestJob()
    167.         {
    168.             collisionWorld = collisionWorld,
    169.             raycastHits = raycastHits,
    170.             raycastInputs = raycastInputs
    171.         }.Schedule(raycastInputs.Length, 4);
    172.  
    173.         return jobhandle;
    174.     }
    175. }
    176.  
    177. [BurstCompile(CompileSynchronously = true)]
    178. public struct UnityRaycastTestJob : IJobParallelFor
    179. {
    180.     [ReadOnly] public CollisionWorld collisionWorld;
    181.     [ReadOnly] public NativeArray<Unity.Physics.RaycastInput> raycastInputs;
    182.  
    183.     public NativeArray<Unity.Physics.RaycastHit> raycastHits;
    184.  
    185.     public void Execute(int index)
    186.     {
    187.         Unity.Physics.RaycastHit raycastHit;
    188.         collisionWorld.CastRay(raycastInputs[index], out raycastHit);
    189.         raycastHits[index] = raycastHit;
    190.     }
    191. }
    I'm curious to know what you're doing different. I imagine number of CPU cores would have an impact. I'm using a 12yo 4core (8 thread) CPU so perhaps I won't see as big of an impact when it comes to using Jobs.

    For sure. This was just a quick mock-up. I don't even have any plans to use so many raycasts. In fact, at the time of even learning about raycasting in DOTS I had only planned to use just a single raycast using collisionWorld.CastRay() so the performance impact is negligible. I was just curious to know how performant it was in comparison to PhysX and when I discovered it was in fact slower I decided to do a little research.

    With that said, I'm wanting to learn DOTS for performance reasons so it is a bit of a downer when I find something which turns out to be slower. But, it's still in development so I'm sure there will be performance improvements in the future.
     
    jasons-novaleaf likes this.
  12. rz_0lento

    rz_0lento

    Joined:
    Oct 8, 2013
    Posts:
    2,136
    More cores don't really help with DOTS today that much. For me it's actually opposite, the more cores and threads you throw at things like (DOTS) Unity Physics and Hybrid Renderer the worse these systems run. I get best results when capping worker thread amount to 1-4 (by default I'd get 23 worker threads on my cpu). Jobs scheduling is bit broken today with DOTS on high core count CPU's (or at least it's really far from "performance by default").
     
  13. Shane_Michael

    Shane_Michael

    Joined:
    Jul 8, 2013
    Posts:
    129
    I imagine the CPU does make some difference, but I think the main factor is that you are copying the native array results back to a managed array to return the values which seems redundant. I just return the allocated native array to be used and disposed.

    More like this and I added the RaycastCommand option too if you want to try that:

    Code (CSharp):
    1. using Unity.Burst;
    2. using Unity.Collections;
    3. using Unity.Entities;
    4. using Unity.Jobs;
    5. using Unity.Physics;
    6. using Unity.Physics.Systems;
    7. using UnityEngine;
    8.  
    9. public class RaycastTest : MonoBehaviour
    10. {
    11.     [SerializeField] private int numRaysCast = 10000;
    12.  
    13.     private BuildPhysicsWorld buildPhysicsWorld => World.DefaultGameObjectInjectionWorld.GetExistingSystem<BuildPhysicsWorld>();
    14.     private CollisionWorld collisionWorld => buildPhysicsWorld.PhysicsWorld.CollisionWorld;
    15.  
    16.     private System.Diagnostics.Stopwatch stopwatch = new System.Diagnostics.Stopwatch();
    17.  
    18.     private string[] debugMessages = new string[] { "PhysX", "Unity Physics", "Unity Physics !Batched", "Unity Physics Batched", "PhysX with RaycastCommand " };
    19.  
    20.     // Update is called once per frame
    21.     private void Update()
    22.     {
    23.         if (Input.GetMouseButtonDown(0))
    24.         {
    25.             UnityEngine.Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
    26.  
    27.             // Distance to cast ray
    28.             float rayDistance = 65535f;
    29.  
    30.             UnityEngine.RaycastHit[] physXHits = new UnityEngine.RaycastHit[numRaysCast];
    31.             Unity.Physics.RaycastHit[] unityHits = new Unity.Physics.RaycastHit[numRaysCast];
    32.             NativeArray<Unity.Physics.RaycastHit> unityNativeHits;
    33.             NativeArray<UnityEngine.RaycastHit> physXNativeHits;
    34.  
    35.             // Raycast Input for Unity Physics
    36.             Unity.Physics.RaycastInput unityRaycastInput = new RaycastInput
    37.             {
    38.                 Start = ray.origin,
    39.                 End = ray.direction * rayDistance,
    40.                 Filter = new CollisionFilter
    41.                 {
    42.                     BelongsTo = ~0u,
    43.                     CollidesWith = ~0u,
    44.                     GroupIndex = 0
    45.                 }
    46.             };
    47.  
    48.             // Perform PhysX Raycasts
    49.             stopwatch.Reset();
    50.             stopwatch.Start();
    51.  
    52.             for (int i = 0; i < numRaysCast; i++)
    53.             {
    54.                 UnityEngine.Physics.Raycast(ray, out physXHits[i], rayDistance);
    55.             }
    56.  
    57.             stopwatch.Stop();
    58.  
    59.             debugMessages[0] = "PhysX Took " + (stopwatch.ElapsedTicks / 10000.0) + "ms";
    60.  
    61.             // Perform Unity Physics Raycasts
    62.             stopwatch.Reset();
    63.             stopwatch.Start();          
    64.             for (int i = 0; i < numRaysCast; i++)
    65.             {
    66.                 collisionWorld.CastRay(unityRaycastInput, out unityHits[i]);
    67.             }          
    68.             stopwatch.Stop();
    69.  
    70.             debugMessages[1] = "Unity Physics Took " + (stopwatch.ElapsedTicks / 10000.0) + "ms";
    71.  
    72.             // Perform Unity Physics Raycasts Jobs without Batching
    73.             stopwatch.Reset();
    74.             stopwatch.Start();
    75.  
    76.             for (int i = 0; i < numRaysCast; i++)
    77.             {
    78.                 SingleRayCast(collisionWorld, unityRaycastInput, out unityHits[i]);
    79.             }
    80.  
    81.             stopwatch.Stop();
    82.  
    83.             debugMessages[2] = "Unity Physics Jobs Without Batching Took " + (stopwatch.ElapsedTicks / 10000.0) + "ms";
    84.  
    85.             // Perform Unity Physics Raycasts Jobs with Batching
    86.             stopwatch.Reset();
    87.             stopwatch.Start();
    88.  
    89.             MultiRayCast(numRaysCast, collisionWorld, unityRaycastInput, out unityNativeHits);
    90.             unityNativeHits.Dispose();
    91.  
    92.             stopwatch.Stop();
    93.  
    94.             debugMessages[3] = "Unity Physics Jobs With Batching Took " + (stopwatch.ElapsedTicks / 10000.0) + "ms";
    95.  
    96.             stopwatch.Reset();
    97.             stopwatch.Start();
    98.             RaycastCommandCast(numRaysCast, ray, rayDistance, out physXNativeHits);
    99.             physXNativeHits.Dispose();
    100.             stopwatch.Stop();
    101.  
    102.             debugMessages[4] = "PhysX with RaycastCommand Took " + (stopwatch.ElapsedTicks / 10000.0) + "ms";
    103.         }
    104.     }
    105.  
    106.     private void OnGUI()
    107.     {
    108.         GUIStyle style = new GUIStyle();
    109.         style.fontSize = 20;
    110.         style.fontStyle = FontStyle.Bold;
    111.  
    112.         style.normal.textColor = Color.white;
    113.  
    114.         GUI.Label(new Rect(10, 20, Screen.width - 10, 20), "Rays Cast: " + numRaysCast, style);
    115.         GUI.Label(new Rect(10, 40, Screen.width - 10, 20), debugMessages[0], style);
    116.         GUI.Label(new Rect(10, 60, Screen.width - 10, 20), debugMessages[1], style);
    117.         GUI.Label(new Rect(10, 80, Screen.width - 10, 20), debugMessages[2], style);
    118.         GUI.Label(new Rect(10, 100, Screen.width - 10, 20), debugMessages[3], style);
    119.         GUI.Label(new Rect(10, 120, Screen.width - 10, 20), debugMessages[4], style);
    120.     }
    121.  
    122.     public bool RaycastCommandCast(int numRaysCast, UnityEngine.Ray ray, float rayDistance, out NativeArray<UnityEngine.RaycastHit> raycastHits)
    123.     {
    124.         bool hitDetected = false;
    125.  
    126.         raycastHits = new NativeArray<UnityEngine.RaycastHit>(numRaysCast, Allocator.TempJob);
    127.         var commands = new NativeArray<RaycastCommand>(numRaysCast, Allocator.TempJob);
    128.         RaycastCommand raycastCommand = new RaycastCommand(ray.origin, ray.direction, rayDistance);      
    129.  
    130.         for (int i = 0; i < numRaysCast; i++) commands[i] = raycastCommand;
    131.  
    132.         // Schedule the batch of raycasts
    133.         JobHandle handle = RaycastCommand.ScheduleBatch(commands, raycastHits, 1, default(JobHandle));      
    134.  
    135.         // Wait for the batch processing job to complete
    136.         handle.Complete();
    137.  
    138.         for (int i = 0; i < numRaysCast; i++)
    139.         {
    140.             if (hitDetected) break;
    141.             else
    142.             {
    143.                 hitDetected = (raycastHits[i].collider != null);
    144.             }
    145.         }                      
    146.  
    147.         // Dispose the buffers      
    148.         commands.Dispose();
    149.  
    150.         return hitDetected;
    151.     }
    152.  
    153.     public static bool SingleRayCast(CollisionWorld collisionWorld, Unity.Physics.RaycastInput raycastInput, out Unity.Physics.RaycastHit raycastHit)
    154.     {
    155.         NativeArray<RaycastInput> raycastInputs = new NativeArray<RaycastInput>(1, Allocator.TempJob);
    156.         NativeArray<Unity.Physics.RaycastHit> raycastHits = new NativeArray<Unity.Physics.RaycastHit>(1, Allocator.TempJob);
    157.  
    158.         raycastInputs[0] = raycastInput;
    159.  
    160.         JobHandle jobHandle = ScheduleBatchRaycast(collisionWorld, raycastInputs, raycastHits);
    161.         jobHandle.Complete();
    162.  
    163.         raycastHit = raycastHits[0];
    164.  
    165.         raycastInputs.Dispose();
    166.         raycastHits.Dispose();
    167.  
    168.         return raycastHit.Entity != Entity.Null;
    169.     }
    170.  
    171.     public static bool MultiRayCast(int numRays, CollisionWorld collisionWorld, Unity.Physics.RaycastInput raycastInput, out NativeArray<Unity.Physics.RaycastHit> raycastHits)
    172.     {
    173.         NativeArray<RaycastInput> naRaycastInputs = new NativeArray<RaycastInput>(numRays, Allocator.TempJob);
    174.         raycastHits = new NativeArray<Unity.Physics.RaycastHit>(numRays, Allocator.TempJob);      
    175.  
    176.         bool hitDetected = false;
    177.  
    178.         for (int i = 0; i < numRays; i++)
    179.         {
    180.             naRaycastInputs[i] = raycastInput;
    181.         }
    182.  
    183.         JobHandle jobHandle = ScheduleBatchRaycast(collisionWorld, naRaycastInputs, raycastHits);
    184.         jobHandle.Complete();
    185.  
    186.         for (int i = 0; i < numRays; i++)
    187.         {
    188.             if (hitDetected) break;
    189.             else
    190.             {
    191.                 hitDetected = raycastHits[i].Entity != Entity.Null;
    192.             }
    193.         }
    194.  
    195.         naRaycastInputs.Dispose();      
    196.  
    197.         return hitDetected;
    198.     }
    199.  
    200.     // Shcedule the jobs
    201.     public static JobHandle ScheduleBatchRaycast(CollisionWorld collisionWorld, NativeArray<Unity.Physics.RaycastInput> raycastInputs, NativeArray<Unity.Physics.RaycastHit> raycastHits)
    202.     {
    203.         JobHandle jobhandle = new UnityRaycastTestJob()
    204.         {
    205.             collisionWorld = collisionWorld,
    206.             raycastHits = raycastHits,
    207.             raycastInputs = raycastInputs
    208.         }.Schedule(raycastInputs.Length, 4);
    209.  
    210.         return jobhandle;
    211.     }
    212. }
    213.  
    214. [BurstCompile(CompileSynchronously = true)]
    215. public struct UnityRaycastTestJob : IJobParallelFor
    216. {
    217.     [ReadOnly] public CollisionWorld collisionWorld;
    218.     [ReadOnly] public NativeArray<Unity.Physics.RaycastInput> raycastInputs;
    219.  
    220.     public NativeArray<Unity.Physics.RaycastHit> raycastHits;
    221.  
    222.     public void Execute(int index)
    223.     {
    224.         Unity.Physics.RaycastHit raycastHit;
    225.         collisionWorld.CastRay(raycastInputs[index], out raycastHit);
    226.         raycastHits[index] = raycastHit;
    227.     }
    228. }
     
    rz_0lento likes this.
  14. rz_0lento

    rz_0lento

    Joined:
    Oct 8, 2013
    Posts:
    2,136
    @inSight01 One thing worth noting is that you may want to check your Project Settings -> Burst AOT that it builds standalone with highest CPU architecture your CPU supports. For example x64 defaults for me for SSE2 and AVX2 only.

    I also tested this with @Shane_Michael's script and I get 1ms for PhysX, 0.24ms with RaycastCommand and 0.24ms with Unity Physics Jobs With Batching, making Unity Physics and PhysX pretty equal speed when you jobify them.
     
  15. inSight01

    inSight01

    Joined:
    Apr 18, 2017
    Posts:
    14
    The difference for me was very small. Although I did learn a few things from your modifications so thank you.

    In any case. The results when casting just a single ray are interesting. PhysX (without RaycastCommand) is significantly faster than all of them.





    Curious to know what's happening in the background. Would also be awesome if Unity Physics CastRay(); was as performant comparatively to PhysX.

    In any case. I think this thread is resolved. Much appreciate the help.
     
  16. rz_0lento

    rz_0lento

    Joined:
    Oct 8, 2013
    Posts:
    2,136
    I didn't look much of the code itself but with DOTS and jobs, you need to be careful how you schedule the job.

    If you only want to compute single raycast on DOTS physics, you definitely don't want to involve worker threads for it. Instead you just run the job on main thread and you loose a lot of initial overhead from the job system. Additionally there's also way to schedule job to only run on single worker or split it among all workers, which one of these you pick depends on your workload but running one raycast should definitely just be run from main thread.
     
  17. petarmHavok

    petarmHavok

    Joined:
    Nov 20, 2018
    Posts:
    461
    Love the discussion here folks!

    Just to add to what @Shane_Michael said, single ray vs. cube tests only that primitive case, not really a full ray cast performance. There is so much more in a ray cast (and any other query), filtering out far away colliders, querying more complicated colliders (meshes), you name it.

    Furthermore, shooting the same ray many times is also not fair, since it allows the engines to cache results and predict the code paths easily.

    For example, try a more complicated scene with meshes. You have some meshes in the physics samples, like the LargeMesh one. Also, generate a set of random 10000 rays, out of which some will hit and some will miss (different lengths, different directions). Then run your engines and compare the results. That should give a clear picture of what's faster.

    This way you are just checking which engine was better in ray vs. box, which can be important, but ray vs. mesh is much more complex and also much more used these days.

    In the end, I'm not trying to point out any engine will be faster in this case, just trying to provide some insight on how to do fair comparisons and proper test and get useful information from it.
     
    Bivens32, Nyanpas, GliderGuy and 5 others like this.
unityunity