Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Try to implement BoxCast, but encounter seriously performance problem #1

Open
vhys366lnsh opened this issue Jan 20, 2020 · 1 comment

Comments

@vhys366lnsh
Copy link

BoxCast results are all correct, but performance is unbelievable low
comu1
400 BoxCast
Unity cost 2 ms
Bepu cost 6494 ms, and GC Alloc is 53.2 MB

comu2

bepuphysics2 source code here
GJKDistanceTester.Edge() cost 1753.64 ms, and 3.2MB GC Alloc
comu3

This looks like SIMD things.
I am not familiar with SIMD so I don't know how to modify these code to improve performance in Unity.
You looks pretty cool at these.
Can you give me some advice?
Thanks in advance!

comu4
Here's Bench MonoBehaviour, put it on PhysicsSystem gameobject and click Benchmark, it will start a Benchmark

using UnityEngine;
using System.Collections.Generic;
using BepuPhysics;
using BepuPhysics.Collidables;
using BepuPhysicsUnity;

public class Bench : MonoBehaviour
{
    public bool benchmark;

    PhysicSimulation _physicSimulation;

    List<UnityEngine.Vector3> unityRayStartPositions;
    int unityRayStartPositionsCount;
    RaycastHit unityRaycastHit;
    UnityEngine.Vector3 unityHalfExtents = new UnityEngine.Vector3(1, 1, 1);

    List<System.Numerics.Vector3> bepuRayStartPositions;
    int bepuRayStartPositionsCount;
    System.Numerics.Vector3 Vector3Down = new System.Numerics.Vector3(0, -1, 0);
    System.Numerics.Vector3 bepuHalfExtents = new System.Numerics.Vector3(1, 1, 1);

    int boxCastCount = 400;
    float detectDistance = 30;

    void Start()
    {
        _physicSimulation = GetComponent<PhysicSimulation>();

        unityRayStartPositions = new List<UnityEngine.Vector3>(boxCastCount);
        bepuRayStartPositions = new List<System.Numerics.Vector3>(boxCastCount);

        int loopCount = (int)Mathf.Sqrt(boxCastCount);
        for (int i = 0; i < loopCount; i++)
        {
            for (int j = 0; j < loopCount; j++)
            {
                unityRayStartPositions.Add(new UnityEngine.Vector3(i * 0.1f, 5, j * 0.1f));
                bepuRayStartPositions.Add(new System.Numerics.Vector3(i * 0.1f, 5, j * 0.1f));
            }
        }
        unityRayStartPositionsCount = unityRayStartPositions.Count;
        bepuRayStartPositionsCount = bepuRayStartPositions.Count;

        //Add BoxCollider so Unity Physics.BoxCast can hit it
        BoxDetection[] boxDetections = FindObjectsOfType<BoxDetection>();
        for (int i = 0; i < boxDetections.Length; i++)
        {
            BoxDetection boxDetection = boxDetections[i];
            if (boxDetection.GetComponent<BoxCollider>() == null)
            {
                boxDetection.gameObject.AddComponent<BoxCollider>();
                //BoxDetection size does not match cube mesh, correct it
                boxDetection.SetSize(boxDetection.transform.localScale);
            }
        }
    }

    void Benchmark()
    {
        System.Diagnostics.Stopwatch st = new System.Diagnostics.Stopwatch();

        st.Start();
        BenchmarkUnityBoxCast();
        st.Stop();
        UnityEngine.Debug.LogFormat("BenchmarkUnityBoxCast: {0} ms", st.ElapsedMilliseconds);
        st.Reset();

        st.Start();
        BenchmarkBepuBoxCast();
        st.Stop();
        UnityEngine.Debug.LogFormat("BenchmarkBepuBoxCast: {0} ms", st.ElapsedMilliseconds);
        st.Reset();
    }

    void BenchmarkUnityBoxCast()
    {
        for (int i = 0; i < unityRayStartPositionsCount; i++)
        {
            if (Physics.BoxCast(unityRayStartPositions[i], unityHalfExtents, UnityEngine.Vector3.down, out unityRaycastHit, UnityEngine.Quaternion.identity, detectDistance))
            {
                //UnityEngine.Debug.DrawLine(unityRayStartPositions[i], unityRaycastHit.point, Color.green);
            }
        }
    }

    void BenchmarkBepuBoxCast()
    {
        for (int i = 0; i < bepuRayStartPositionsCount; i++)
        {
            if (BepuBoxCast(bepuRayStartPositions[i], bepuHalfExtents, Vector3Down, BepuUtilities.Quaternion.Identity, detectDistance))
            {
                /*
                UnityEngine.Vector3 start = new Vector3(bepuRayStartPositions[i].X, bepuRayStartPositions[i].Y, bepuRayStartPositions[i].Z);
                UnityEngine.Vector3 end = new Vector3(_boxCastHitHandler.HitLocation.X, _boxCastHitHandler.HitLocation.Y, _boxCastHitHandler.HitLocation.Z);
                UnityEngine.Debug.DrawLine(start, end, Color.red);
                */
            }
        }
    }

    struct SceneSweepHitHandler : ISweepHitHandler
    {
        public System.Numerics.Vector3 HitLocation;
        public System.Numerics.Vector3 HitNormal;
        public float T;

        public bool AllowTest(CollidableReference collidable)
        {
            return true;
        }

        public bool AllowTest(CollidableReference collidable, int child)
        {
            return true;
        }

        public void OnHit(ref float maximumT, float t, in System.Numerics.Vector3 hitLocation, in System.Numerics.Vector3 hitNormal, CollidableReference collidable)
        {
            //Changing the maximum T value prevents the traversal from visiting any leaf nodes more distant than that later in the traversal.
            //It is effectively an optimization that you can use if you only care about the time of first impact.
            if (t < maximumT)
                maximumT = t;
            if (t < T)
            {
                T = t;
                HitLocation = hitLocation;
                HitNormal = hitNormal;
            }
        }

        public void OnHitAtZeroT(ref float maximumT, CollidableReference collidable)
        {
            maximumT = 0;
            T = 0;
            HitLocation = new System.Numerics.Vector3();
            HitNormal = new System.Numerics.Vector3();
        }
    }

    SceneSweepHitHandler _boxCastHitHandler = new SceneSweepHitHandler();
    Box _boxShape = new Box(1, 1, 1);
    RigidPose _boxRigidPose = new RigidPose();
    BodyVelocity _boxBodyVelocity = new BodyVelocity();
    unsafe bool BepuBoxCast(System.Numerics.Vector3 origin, System.Numerics.Vector3 halfExtents, System.Numerics.Vector3 direction, BepuUtilities.Quaternion orientation, float maxDistance)
    {
        _boxCastHitHandler.T = float.MaxValue;

        _boxShape.HalfWidth = halfExtents.X;
        _boxShape.HalfHeight = halfExtents.Y;
        _boxShape.HalfLength = halfExtents.Z;

        _boxRigidPose.Position = origin;
        _boxRigidPose.Orientation = orientation;

        _boxBodyVelocity.Linear = direction;

        _physicSimulation.Simulation.Sweep(_boxShape, _boxRigidPose, _boxBodyVelocity, maxDistance, _physicSimulation.BufferPool, ref _boxCastHitHandler);

        if (_boxCastHitHandler.T < float.MaxValue && _boxCastHitHandler.T > 0)
        {
            return true;
        }
        else
        {
            return false;
        }
    }

    void Update()
    {
        if (benchmark)
        {
            Benchmark();
            benchmark = false;
            //UnityEngine.Debug.Break();
        }
    }
}
@AntoineCharton
Copy link
Owner

Hey,

Thanks for reporting it.

It's in deed an issue with SIMD that's disabled in Unity. I don't have a magical solution for this right now but I have a couple of leads to improve the speed.
Utilizing SIMD/SSE in Unity3D (.NET 2.0).

I will keep you updated on this matter :) .

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants