Search Unity

Floating Point Errors and Large Scale Worlds

Discussion in 'World Building' started by Phelan-Simpson, Apr 15, 2018.

  1. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    17
    I have measured the moving collider performance issue raised by @Marcos-Elias, @ristophonics, and @razzraziel. With 10k collider objects (boxes with a collider component), if I continuously transform the group by transforming the scene root, there is a significant physics load seen in the profiler. As I did in my latest paper, I ran it on my core i5 laptop and, with very little geometry visible, and used the mouse keys to continually move the scene root.

    In the first image attached, on the left of the screen can be seen the simple visible geometry and profiler window on right.

    CollidersActiveHierarchy-30pc.jpg

    You need to tell Unity to not perform unnecessary processing when the scene root is moved and this can be by turning off the active hierarchy above the colliders before each transform and back on after it:
    Code (CSharp):
    1. nucleonsGroup.SetActive(false);
    2. // Move the scene to the new position by changing scene parent object transform.
    3. transform.Translate(rotated_transform);
    4.  
    5.  nucleonsGroup.SetActive(true);
    The next image shows what happens when I do this.


    The profiler measurements show that the magnitude of processing devoted to collider movement is now much less compared to the proportion of cpu devoted to rendering a few simple objects, even while continuously transforming 10,000 colliders. Therefore, moving colliders does not need to cause a significant performance load.
     

    Attached Files:

    Last edited: Sep 8, 2019
  2. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    4,005
    Did you profile it in a build or editor?

    I'm asking because:
     
  3. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    17
    @Peter77 Good question, I am running from the editor play button because, even though I have developer build ticked and autoconnect profiler ticked, no profiler is displayed when I run it as an app.

    I run it full screen, so the rendering of editor windows superpig mentioned is not an issue.
    I also turn vsync off so there are no cpu spikes from that.
    There are no memory allocations during run either, after the initial creation of 10k colliders.
    There may be other interference from the editor but, on average, the relative load comparisons are still valid anyway.

    The doco on profiling with dev build talks about running profiler on another machine, which would be ideal but I do not have a network setup.

    If you can tell me how to get the profiler up on the same machine when running from a built app then I will try it.
     
  4. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    4,005
    That should be no different:
    • Start development build
    • Open Profiler window
    • Select Player in Profiler drop-down
    • Press record in Profiler window
     
  5. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    17
    It is on my system: after starting it there is no menu option to open profiler. Is there a KB shortcut?
    No menu options for anything. OSX Mojave 10.14.6. Unity 2018.4.7.f1
     
  6. Peter77

    Peter77

    Joined:
    Jun 12, 2013
    Posts:
    4,005
    You're saying when you start the build, the "Windows > Analysis > Profiler" menu item disappears from the Unity Editor main menu? :eek:
     
  7. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    17
    I mean it is not available in the build that I run. If I need to run it from the editor (like I have been doing) then ... do you mean I should close the editor after starting profiler and it will connect with the build when I run that?
     
  8. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    17
    @Peter77: I worked it out. Here are the profile measurements from two dev builds: the first with an active hierarchy during moves and the second with it inactive.
    Profile with active 10k colliders under root node while moving (the hierarchy).
    HierarchyActive2-moving40pc.jpg

    Profile for inactive hierarchy:

    HierarchyInactiveUnity2018.7-40pc.jpg

    So the difference is 60fps+ (active hierarchy) to 200fps+ (inactive).

    For these tests, I used 1280x800 with rendering quality set to very low. Same version of Unity 2018.7. Only apps running were the unity editor with the profiler, the game from dev build.
     

    Attached Files:

    Peter77 likes this.
  9. cosmochristo

    cosmochristo

    Joined:
    Sep 24, 2018
    Posts:
    17
    I put an example video online here:


    The relevant scripts shown attached in video are:
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. /*
    6. * Performs simple Floating Origin player movement by reverse transforming the scene
    7. * Floating origin movement only happens if there is
    8. * a change in navigation input and it is above a minimum threshold.
    9. *
    10. * Here's and online example testing 10k colliders: https://youtu.be/DsWLQFnRdLo
    11. * which shows how the PlayerMove and PlayerView are attached.
    12. *
    13. * Assumptions:
    14. * Player object has camera attached, it is positioned at the origin, and
    15. * tagged "playerCapsule".
    16. * This script is attached to the scene parent object.
    17. */
    18. public class PlayerMove : MonoBehaviour
    19. {
    20.  
    21.     // Minimum player collision detection distance
    22.     private const float COLLISION_DISTANCE = 2f;
    23.     // Multiplier for when bouncing back from collisions
    24.     private const float COLLISION_ADJUST = 1.2f;
    25.     // Threshold for detecting navigation changes
    26.     private const float NAV_SHAKE_THRESHOLD = 0.0001f;
    27.     // Max distance for detecting player collisions
    28.     private const float RAYCAST_DISTANCE = 10f;
    29.  
    30.     // Multiplier to each movement input
    31.     private readonly float speed = 7.0f;
    32.     private GameObject player;
    33.     private GameObject SceneRoot;
    34.     // Layer mask for ray casting
    35.     private int layerMask;
    36.     // Horizontal movement deltas
    37.     private float deltaX;
    38.     private float deltaZ;
    39.     private float speedAdj;
    40.     // Current reverse transform
    41.     private Vector3 reverseMovement;  
    42.     // Rotated reverse transform
    43.     private Vector3 rotated_transform = new Vector3(0f,0f,0f);
    44.     private readonly Vector3 player_position = new Vector3(0f, 0f, 0f);
    45.     private RaycastHit rayCollision;
    46.  
    47.     void Start()
    48.     {
    49.         // Accloc mem once only
    50.         reverseMovement = new Vector3(0, 0, 0);
    51.  
    52.         // Use Physics.Raycast to cast a ray forward into scene to check for collisions
    53.         // create a bit mask for 7 layers with 0 for player layer to use in Raycast
    54.         layerMask = 1 << 8;
    55.         layerMask = ~layerMask;
    56.  
    57.         // turn off cursor display in game window
    58.         Cursor.visible = false;
    59.  
    60.         // Get access to the player object
    61.         player = GameObject.FindGameObjectWithTag("playerCapsule");
    62.         SceneRoot = GameObject.FindGameObjectWithTag("root");
    63.  
    64.         if (player == null)
    65.         {
    66.             print("player not found");
    67.         }
    68.     }
    69.  
    70.     /// <summary>
    71.     /// Do not use FixedUpdate here because performance drops dramatically (on dual core i5 macbook pro).
    72.     /// <seealso cref="PlayerView.cs"/>
    73.     /// </summary>
    74.     void Update()
    75.     {
    76.         // Get the horizontal movement changes from keyboard and
    77.         // negate them so we can move scene in reverse
    78.         deltaX = -Input.GetAxis("Horizontal");
    79.         deltaZ = -Input.GetAxis("Vertical");
    80.  
    81.         // Only process floating origin movement if there is navigation input
    82.         // change and it is above noise/shake threshold.
    83.         // Performance: don't really want a sqr root here -
    84.         //   or even a squares comparision.
    85.         if ((Mathf.Abs(deltaX) + Mathf.Abs(deltaZ)) > NAV_SHAKE_THRESHOLD)
    86.         {
    87.  
    88.             speedAdj = Time.deltaTime * speed;
    89.  
    90.             // Scene reverse transform for floating origin navigation.
    91.             // Make movement delta proportional to time since last move and speed factor.
    92.             // Peformance: changed this to assignment so no mem alloc and GC needed, and
    93.             // 2 multiplies a bit faster than multiply by 3D vector.
    94.             reverseMovement.x = deltaX * speedAdj;
    95.             reverseMovement.z = deltaZ * speedAdj;
    96.            
    97.             /*// Uncomment to do player collision detection.
    98.               //If player collided with close object then ...
    99.             if (Physics.Raycast(player_position, player.transform.TransformDirection(Vector3.forward), out rayCollision, COLLISION_DISTANCE, layerMask)
    100.                 && (rayCollision.distance < COLLISION_DISTANCE))
    101.             {
    102.                 /// ... bounce back a little from collision
    103.                 transform.Translate(-rotated_transform*COLLISION_ADJUST);
    104.             }
    105.             else // no collision, so move scene in reverse
    106.             {*/
    107.                 // use player camera rotation to modify reverse movement vector so that player forward corresponds to forward movement input
    108.                 rotated_transform = Quaternion.Euler(player.transform.localEulerAngles) * reverseMovement;
    109.  
    110.                 SceneRoot.SetActive(false);
    111.  
    112.                 // Move the scene to the new position by changing scene parent object transform.
    113.                 transform.Translate(rotated_transform);
    114.  
    115.                 SceneRoot.SetActive(true);
    116.             /*}*/
    117.         }
    118.     }
    119. }
    and
    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. /*
    6. * Rotates the player, and hence attached player view, based on mouse input.
    7. *
    8. * Assumptions:
    9. * This script is attached to player object.
    10. * Camera is attached to player object.
    11. */
    12. public class PlayerView : MonoBehaviour
    13. {
    14.  
    15.     // Control how fast player view responds to mouse movement
    16.     // More sophisticated games would allow the sensitivity to be changed to suit a player's preferences.
    17.     private const float sensitivity = 2f;
    18.  
    19.     // Limit (clamp) the vertical rotation to +/- this angle
    20.     private const float vertClamp = 60.0f;
    21.  
    22.     // Rotation about the horizontal axis (up and down)
    23.     private float currentRotationX = 0;
    24.     // Left and right rotation
    25.     private float currentRotationY = 0;
    26.  
    27.     void Update()
    28.     {
    29.         // horizontal and vertical rotation at the same time
    30.         currentRotationX -= Input.GetAxis("Mouse Y") * sensitivity;
    31.         currentRotationX = Mathf.Clamp(currentRotationX, -vertClamp, vertClamp);
    32.  
    33.         currentRotationY = transform.localEulerAngles.y + Input.GetAxis("Mouse X") * sensitivity;
    34.  
    35.         transform.localEulerAngles = new Vector3(currentRotationX, currentRotationY, 0);
    36.     }
    37. }
    38.  
     
    Last edited: Sep 13, 2019 at 5:01 AM