AntoineCharton / Bepuphysics-Unity

A bridge for Bepuphysics and Unity
Apache License 2.0
64 stars 23 forks source link

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

Open vhys366lnsh opened 4 years ago

vhys366lnsh commented 4 years ago

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 commented 4 years ago

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 :) .