Job returns different data when compiled with Burst

Hi, I’ve recently attempted to translate a C++ mesh simplification algorithm to the Unity Job System and Burst. The algorithm works perfectly in a the when burst is disabled, however, when I enable Burst the resulting meshes are very different to the non burst versions. I have tried all different floating point precision compilation options and nothing is able to replicate the non burst version of the job.

At this point I’m pretty stumped. I don’t know if this is a bug with Burst or something undocumented that I’m doing wrong. Debugging is pretty difficult given the algorithm is somewhat complex and the error doesn’t present when not compiled in Burst… If anyone has experienced this before, has any ideas, or feels like taking a look at a sample project, I have uploaded it here. Feedback would be much appreciated!

Here are some examples of differences in outputs between the non Burst and the Burst jobs:
Non Burst:


Burst:

Rear view of meshes, Burst is on the left:

The linked project has a simple sample scene, you can assign the mesh gameobjects (name: ll1) to the gameobject (name: GameObject) with the SimpJob script on it, then hit run. Try this with and without burst enabled to see the different results.

4800830–458990–sample.zip (348 KB)

1 Like

Just post your code in code tags in the future, most of us lazy here:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Jobs;
using Unity.Burst;
using Unity.Mathematics;
using Unity.Jobs;
using Unity.Collections;
using Unity.Collections.LowLevel.Unsafe;

public class SimpJob : MonoBehaviour
{
    public float targ = 0.7f;
    public float agg = 7;
    public bool runTest;
    public GameObject o;

    public void Update()
    {
        if (runTest)
        {
            runTest = false;
            Mesh m = o.GetComponent<MeshFilter>().mesh;
            Test(m, targ);
        }
    }

    public unsafe void Test(Mesh input, float t)
    {
        NativeArray<int> triangles = new NativeArray<int>((int)input.GetIndexCount(0), Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
        NativeArray<float3> vertices = new NativeArray<float3>(input.vertexCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
        NativeArray<float3> normals = new NativeArray<float3>(input.vertexCount, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
        NativeArray<int> results = new NativeArray<int>(3, Allocator.Persistent, NativeArrayOptions.UninitializedMemory);
        int totalTris = (int)input.GetIndexCount(0) / 3;

        var simpJob = new SimplifyJob
        {
            target_count = (int)(totalTris * t),
            triangles = triangles,
            vertices = vertices,
            normals = normals,
            results = results,
            meshVerts = UnsafeUtility.AddressOf(ref input.vertices[0]),
            meshTris = UnsafeUtility.AddressOf(ref input.triangles[0]),
            meshNorms = UnsafeUtility.AddressOf(ref input.normals[0]),
            agressiveness = agg
        }.Schedule();

        simpJob.Complete();

        int triCount = results[0];
        int vertCount = results[1];
        int normCount = vertCount;

        input.Clear();

        input.SetVertices(vertices, 0, vertCount);
        input.SetIndices(triangles, 0, triCount * 3, MeshTopology.Triangles, 0, true);
        input.SetNormals(normals, 0, normCount);

        triangles.Dispose();
        vertices.Dispose();
        normals.Dispose();
        results.Dispose();
    }

    public struct Ref
    {
        public int tid, tvertex;
        public void Set(int tid, int tvertex)
        {
            this.tid = tid;
            this.tvertex = tvertex;
        }
    }

    [BurstCompile]
    public unsafe struct SimplifyJob : IJob
    {
        public NativeArray<int> results;
        public NativeArray<int> triangles;
        public NativeArray<float3> vertices;
        public NativeArray<float3> normals;
        [ReadOnly, NativeDisableUnsafePtrRestriction] public void* meshVerts;
        [ReadOnly, NativeDisableUnsafePtrRestriction] public void* meshTris;
        [ReadOnly, NativeDisableUnsafePtrRestriction] public void* meshNorms;
        int triCount;
        int vertCount;
        public int target_count;
        public float agressiveness;

        //
        // Main simplification function
        //
        // target_count  : target nr. of triangles
        // agressiveness : sharpness to increase the threshold.
        //                 5..8 are good numbers
        //                 more iterations yield higher quality
        //
        public void Execute()
        {
            int indiceCount = triangles.Length;
            triCount = indiceCount / 3;
            vertCount = vertices.Length;
            int triangle_count = triCount;

            NativeArray<float> triErr = new NativeArray<float>(triCount * 4, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
            NativeArray<bool> vborder = new NativeArray<bool>(vertCount, Allocator.Temp);
            NativeArray<bool> deletedTri = new NativeArray<bool>(triCount, Allocator.Temp);
            NativeArray<float3> triNorm = new NativeArray<float3>(triCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

            NativeArray<float> syMatrix = new NativeArray<float>(vertCount * 10, Allocator.Temp);
            NativeArray<float> tempMatrix = new NativeArray<float>(10, Allocator.Temp);
            NativeList<Ref> refs = new NativeList<Ref>(Allocator.Temp);
            NativeArray<bool> deleted0 = new NativeArray<bool>(50, Allocator.Temp);
            NativeArray<bool> deleted1 = new NativeArray<bool>(50, Allocator.Temp);
            NativeArray<bool> dirtyTris = new NativeArray<bool>(triCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

            NativeArray<int> tcounts = new NativeArray<int>(vertCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
            NativeArray<int> tstarts = new NativeArray<int>(vertCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);
            NativeArray<int> vqs = new NativeArray<int>(vertCount, Allocator.Temp, NativeArrayOptions.UninitializedMemory);

            if (agressiveness < 1)
                agressiveness = 7;

            for (int i = 0; i < indiceCount; i++)
            {
                refs.Add(new Ref());
                triangles[i] = UnsafeUtility.ReadArrayElement<int>(meshTris, i);
                if (i < vertCount)
                {
                    vertices[i] = UnsafeUtility.ReadArrayElement<float3>(meshVerts, i);
                    normals[i] = UnsafeUtility.ReadArrayElement<float3>(meshNorms, i);
                }
            }

            // add one extra
            refs.Add(new Ref());

            // main iteration loop
            int deleted_triangles = 0;

            for (int iteration = 0; iteration < 100; iteration++)
            {
                if (triangle_count - deleted_triangles <= target_count) break;

                // clear dirty flag
                for (int i = 0; i < triCount; i++)
                {
                    dirtyTris[i] = false;
                }

                if (iteration % 5 == 0) update_mesh(iteration, deletedTri, triErr, triNorm, vborder, refs, syMatrix, tempMatrix, tstarts, tcounts, vqs);

                // All triangles with edges below the threshold will be removed
                //
                // The following numbers works well for most models.
                // If it does not, try to adjust the 3 parameters

                double threshold = 0.000000001 * math.pow((iteration + 3), agressiveness);

                // remove vertices & mark deleted triangles
                for (int i = 0; i < triCount; i++)
                {
                    int tvi = i * 3; // tri vertex index
                    int tei = i * 4; // tri error index
                    if (triErr[tei + 3] > threshold)
                    {
                        continue;
                    }
                    if (deletedTri[i])
                    {
                        continue;
                    }
                    if (dirtyTris[i])
                    {
                        continue;
                    }

                    for (int j = 0; j < 3; j++)
                    {
                        if (triErr[tei + j] < threshold)
                        {
                            int i0 = triangles[tvi + j];
                            int i1 = triangles[tvi + ((j + 1) % 3)];

                            // Border check
                            if (vborder[i0] || vborder[i1]) continue;

                            // Compute vertex to collapse to
                            float3 p;
                            calculate_error(i0, i1, out p, vborder, syMatrix, tempMatrix, vqs);

                            // don't remove if flipped
                            if (flipped(p, i0, i1, i0, deleted0, deletedTri, triNorm, refs, tstarts, tcounts)) continue;
                            if (flipped(p, i1, i0, i1, deleted1, deletedTri, triNorm, refs, tstarts, tcounts)) continue;

                            // calculate barycentric coords
                            int i2 = triangles[tvi + ((j + 2) % 3)];
                            float3 barycentricCoord = CalculateBarycentricCoords(p, vertices[i0], vertices[i1], vertices[i2]);

                            // not flipped, so remove edge
                            vertices[i0] = p;
                            AddMatrixReplace(vqs[i0], vqs[i1], syMatrix);

                            // record and interpolate normal
                            int ia0 = i0;// t.v[j];
                            int ia1 = i1;// t.v[(j + 1) % 3];
                            int ia2 = i2;// t.v[(j + 2) % 3];
                            InterpolateVertexAttributes(ia0, ia0, ia1, ia2, barycentricCoord);

                            int tstart = refs.Length;

                            deleted_triangles += update_triangles(i0, i0, deleted0, deletedTri, triErr, vborder, refs, syMatrix, tempMatrix, tstarts, tcounts, dirtyTris, vqs);
                            deleted_triangles += update_triangles(i0, i1, deleted1, deletedTri, triErr, vborder, refs, syMatrix, tempMatrix, tstarts, tcounts, dirtyTris, vqs);

                            int tcount = refs.Length - tstart;

                            if (tcount <= tcounts[i0])
                            {
                                // save ram
                                if (tcount > 0)
                                {
                                    NativeArray<Ref>.Copy(refs, tstart, refs, tstarts[i0], tcount);
                                }
                            }
                            else
                                // append
                                tstarts[i0] = tstart;

                            tcounts[i0] = tcount;
                            break;
                        }
                    }
                    // done?
                    if (triangle_count - deleted_triangles <= target_count) break;
                }
                //results[2] = iteration;
                // Debug.Log("iteration count " + iteration + " deleted "+ deleted_triangles);
            }
            // clean up mesh
            compact_mesh(deletedTri, triErr, triNorm, tstarts, tcounts);

            // dispose temp arrays
            refs.Dispose();
            syMatrix.Dispose();
            tempMatrix.Dispose();
            deleted0.Dispose();
            deleted1.Dispose();
            dirtyTris.Dispose();
            tstarts.Dispose();
            tcounts.Dispose();
            vqs.Dispose();
            triErr.Dispose();
            vborder.Dispose();
            deletedTri.Dispose();
            triNorm.Dispose();
        }

        [BurstCompile]
        void compact_mesh(NativeArray<bool> deletedTri, NativeArray<float> triErr, NativeArray<float3> triNorm, NativeArray<int> tstarts, NativeArray<int> tcounts)
        {
            int dst = 0;
            for (int i = 0; i < vertCount; i++)
            {
                tcounts[i] = 0;
            }
            for (int i = 0; i < triCount; i++)
            {
                if (!deletedTri[i])
                {
                    int ti1 = i * 3;
                    int ti2 = dst * 3;
                    // verts
                    triangles[ti2] = triangles[ti1];
                    triangles[ti2 + 1] = triangles[ti1 + 1];
                    triangles[ti2 + 2] = triangles[ti1 + 2];

                    ti1 = i * 4;
                    ti2 = dst * 4;
                    // errs
                    triErr[ti2] = triErr[ti1];
                    triErr[ti2 + 1] = triErr[ti1 + 1];
                    triErr[ti2 + 2] = triErr[ti1 + 2];
                    triErr[ti2 + 3] = triErr[ti1 + 3];

                    triNorm[dst] = triNorm[i];
                    deletedTri[dst] = false;

                    ti1 = i * 3;
                    for (int j = 0; j < 3; j++)
                    {
                        tcounts[triangles[ti1 + j]] = 1;
                    }

                    dst++;
                }
            }

            triCount = dst;

            dst = 0;
            for (int i = 0; i < vertCount; i++)
            {
                if (tcounts[i] > 0)
                {
                    tstarts[i] = dst;
                    vertices[dst] = vertices[i];

                    if (dst != i)
                    {
                        normals[dst] = normals[i];
                    }

                    dst++;
                }
            }
            for (int i = 0; i < triCount; i++)
            {
                int ti = i * 3;
                for (int j = 0; j < 3; j++)
                {
                    triangles[ti + j] = tstarts[triangles[ti + j]];
                }
            }

            vertCount = dst;

            results[0] = triCount;
            results[1] = vertCount;
        }

        [BurstCompile]
        void update_mesh(int iteration, NativeArray<bool> deletedTri, NativeArray<float> triErr, NativeArray<float3> triNorm, NativeArray<bool> vborder, NativeList<Ref> refs, NativeArray<float> syMatrix, NativeArray<float> tempMatrix, NativeArray<int> tstarts, NativeArray<int> tcounts, NativeArray<int> vqs)
        {
            if (iteration > 0) // compact triangles
            {
                int dst = 0;
                for (int i = 0; i < triCount; i++)
                {
                    if (!deletedTri[i])
                    {
                        int ti1 = i * 3;
                        int ti2 = dst * 3;
                        // verts
                        triangles[ti2] = triangles[ti1];
                        triangles[ti2 + 1] = triangles[ti1 + 1];
                        triangles[ti2 + 2] = triangles[ti1 + 2];

                        ti1 = i * 4;
                        ti2 = dst * 4;
                        // errs
                        triErr[ti2] = triErr[ti1];
                        triErr[ti2 + 1] = triErr[ti1 + 1];
                        triErr[ti2 + 2] = triErr[ti1 + 2];
                        triErr[ti2 + 3] = triErr[ti1 + 3];

                        triNorm[dst] = triNorm[i];
                        deletedTri[dst] = false;

                        dst++;
                    }
                }
                triCount = dst;
            }

            // Init Quadrics by Plane & Edge Errors
            //
            // required at the beginning ( iteration == 0 )
            // recomputing during the simplification is not required,
            // but mostly improves the result for closed meshes
            //
            if (iteration == 0)
            {
                for (int i = 0; i < vertCount; i++)
                {
                    vqs[i] = NewSymmetricMatrix(i);
                }
                NativeArray<float3> p = new NativeArray<float3>(3, Allocator.Temp);
                for (int i = 0; i < triCount; i++)
                {
                    int ti = i * 3;
                    float3 n;

                    for (int j = 0; j < 3; j++)
                    {
                        p[j] = vertices[triangles[ti + j]];
                    }

                    n = math.cross(p[1] - p[0], p[2] - p[0]);
                    n = math.normalize(n);

                    triNorm[i] = n;

                    for (int j = 0; j < 3; j++)
                    {
                        AssignTempMatrix(n.x, n.y, n.z, -math.dot(n, p[0]), tempMatrix);
                        AddMatrixReplace(vqs[triangles[ti + j]], syMatrix, tempMatrix);
                    }
                }
                p.Dispose();
                for (int i = 0; i < triCount; i++)
                {
                    // Calc Edge Error
                    int ti = i * 3;
                    int te = i * 4;
                    float3 p1;
                    for (int j = 0; j < 3; j++)
                    {
                        triErr[te + j] = calculate_error(triangles[ti + j], triangles[ti + ((j + 1) % 3)], out p1, vborder, syMatrix, tempMatrix, vqs);
                    }
                    triErr[te + 3] = math.min(triErr[te], math.min(triErr[te + 1], triErr[te + 2]));
                }
            }

            // Init Reference ID list
            for (int i = 0; i < vertCount; i++)
            {
                tstarts[i] = 0;
                tcounts[i] = 0;
            }
            for (int i = 0; i < triCount; i++)
            {
                int ti = i * 3;
                for (int j = 0; j < 3; j++)
                {
                    tcounts[triangles[ti + j]]++;
                }
            }
            int tstart = 0;
            for (int i = 0; i < vertCount; i++)
            {
                tstarts[i] = tstart;
                tstart += tcounts[i];
                tcounts[i] = 0;
            }

            // Write References
            for (int i = 0; i < triCount; i++)
            {
                int ti = i * 3;
                for (int j = 0; j < 3; j++)
                {
                    int vi = triangles[ti + j];
                    Ref r = new Ref();
                    r.tid = i;
                    r.tvertex = j;
                    refs[tstarts[vi] + tcounts[vi]] = r;
                    tcounts[vi]++;
                }
            }

            // Identify boundary : vertices[].border=0,1
            if (iteration == 0)
            {
                NativeList<int> vcount = new NativeList<int>(Allocator.Temp);
                NativeList<int> vids = new NativeList<int>(Allocator.Temp);

                for (int i = 0; i < vertCount; i++)
                {
                    vcount.Clear();
                    vids.Clear();
                    for (int j = 0; j < tcounts[i]; j++)
                    {
                        int k1 = refs[tstarts[i] + j].tid;
                        int ti = k1 * 3;
                        for (int k = 0; k < 3; k++)
                        {
                            int ofs = 0, id = triangles[ti + k];
                            while (ofs < vcount.Length)
                            {
                                if (vids[ofs] == id) break;
                                ofs++;
                            }
                            if (ofs == vcount.Length)
                            {
                                vcount.Add(1);
                                vids.Add(id);
                            }
                            else
                                vcount[ofs]++;
                        }
                    }
                    for (int j = 0; j < vcount.Length; j++)
                    {
                        if (vcount[j] == 1)
                        {
                            vborder[vids[j]] = true;
                        }
                    }
                }
                vcount.Dispose();
                vids.Dispose();
            }
        }

        // this has changed from result to not being out result
        [BurstCompile]
        float calculate_error(int id_v1, int id_v2, out float3 result, NativeArray<bool> vborder, NativeArray<float> syMatrix, NativeArray<float> tempMatrix, NativeArray<int> vqs)
        {
            // compute interpolated vertex
            AssignTempMatrixByAdd(vqs[id_v1], vqs[id_v2], syMatrix, tempMatrix);

            bool borderEdge = vborder[id_v1] & vborder[id_v2];
            float error = 0.0f;
            float det = Determinant1(tempMatrix);

            if (det != 0.0 && !borderEdge)
            {
                // q_delta is invertible
                result = new float3(
                    (-1.0f / det * Determinant2(tempMatrix)),  // vx = A41/det(q_delta)
                    (1.0f / det * Determinant3(tempMatrix)),   // vy = A42/det(q_delta)
                    (-1.0f / det * Determinant4(tempMatrix))); // vz = A43/det(q_delta)

                error = VertexError(result.x, result.y, result.z, tempMatrix);
            }
            else
            {
                // det = 0 -> try to find best result
                float3 p1 = vertices[id_v1];
                float3 p2 = vertices[id_v2];
                float3 p3 = (p1 + p2) * 0.5f;
                float error1 = VertexError(p1.x, p1.y, p1.z, tempMatrix);
                float error2 = VertexError(p2.x, p2.y, p2.z, tempMatrix);
                float error3 = VertexError(p3.x, p3.y, p3.z, tempMatrix);
                error = Min(error1, error2, error3);
                if (error == error3)
                {
                    result = p3;
                }
                else if (error == error2)
                {
                    result = p2;
                }
                else if (error == error1)
                {
                    result = p1;
                }
                else
                {
                    result = p3;
                }
            }
            return error;
        }

        // Update triangle connections and edge error after a edge is collapsed
        [BurstCompile]
        int update_triangles(int i0, int v, NativeArray<bool> deleted, NativeArray<bool> deletedTri, NativeArray<float> triErr, NativeArray<bool> vborder, NativeList<Ref> refs, NativeArray<float> syMatrix, NativeArray<float> tempMatrix, NativeArray<int> tstarts, NativeArray<int> tcounts, NativeArray<bool> dirtyTris, NativeArray<int> vqs)
        {
            int deletedCount = 0;
            float3 p;
            for (int k = 0; k < tcounts[v]; k++)
            {
                Ref r = refs[tstarts[v] + k];
                if (deletedTri[r.tid]) continue;
                if (deleted[k])
                {
                    deletedTri[r.tid] = true;
                    deletedCount++;
                    continue;
                }
                int tvi = r.tid * 3;
                int tei = r.tid * 4;

                triangles[tvi + r.tvertex] = i0;
                dirtyTris[r.tid] = true;
                triErr[tei] = calculate_error(triangles[tvi], triangles[tvi + 1], out p, vborder, syMatrix, tempMatrix, vqs);
                triErr[tei + 1] = calculate_error(triangles[tvi + 1], triangles[tvi + 2], out p, vborder, syMatrix, tempMatrix, vqs);
                triErr[tei + 2] = calculate_error(triangles[tvi + 2], triangles[tvi], out p, vborder, syMatrix, tempMatrix, vqs);
                triErr[tei + 3] = math.min(triErr[tei], math.min(triErr[tei + 1], triErr[tei + 2]));
                refs.Add(r);
            }
            return deletedCount;
        }

        // Check if a triangle flips when this edge is removed
        [BurstCompile]
        bool flipped(float3 p, int i0, int i1, int v0, NativeArray<bool> deleted, NativeArray<bool> deletedTri, NativeArray<float3> triNorm, NativeList<Ref> refs, NativeArray<int> tstarts, NativeArray<int> tcounts)
        {
            for (int k = 0; k < tcounts[v0]; k++)
            {
                int ti = refs[tstarts[v0] + k].tid * 3;
                if (deletedTri[refs[tstarts[v0] + k].tid]) continue;

                int s = refs[tstarts[v0] + k].tvertex;
                int id1 = triangles[ti + ((s + 1) % 3)];
                int id2 = triangles[ti + ((s + 2) % 3)];

                if (id1 == i1 || id2 == i1) // delete ?
                {
                    deleted[k] = true;
                    continue;
                }
                float3 d1 = vertices[id1] - p;
                d1 = math.normalize(d1);
                float3 d2 = vertices[id2] - p;
                d2 = math.normalize(d2);
                float dot = math.dot(d1, d2);
                if (math.abs(dot) > 0.999) return true;

                float3 n = math.cross(d1, d2);
                n = math.normalize(n);
                deleted[k] = false;
                if (math.dot(n, triNorm[refs[tstarts[v0] + k].tid]) < 0.2f) return true;
            }
            return false;
        }

        [BurstCompile]
        private float VertexError(float x, float y, float z, NativeArray<float> tempMatrix)
        {
            return tempMatrix[0] * x * x + 2 * tempMatrix[1] * x * y + 2 * tempMatrix[2] * x * z + 2 * tempMatrix[3] * x + tempMatrix[4] * y * y
                + 2 * tempMatrix[5] * y * z + 2 * tempMatrix[6] * y + tempMatrix[7] * z * z + 2 * tempMatrix[8] * z + tempMatrix[9];
        }

        [BurstCompile]
        public float Min(float val1, float val2, float val3)
        {
            return (val1 < val2 ? (val1 < val3 ? val1 : val3) : (val2 < val3 ? val2 : val3));
        }

        [BurstCompile]
        private void InterpolateVertexAttributes(int dst, int i0, int i1, int i2, float3 barycentricCoord)
        {
            normals[dst] = math.normalize((normals[i0] * barycentricCoord.x) + (normals[i1] * barycentricCoord.y) + (normals[i2] * barycentricCoord.z));
        }

        [BurstCompile]
        private float3 CalculateBarycentricCoords(float3 point, float3 a, float3 b, float3 c)
        {
            float3 v0 = (b - a), v1 = (c - a), v2 = (point - a);
            float d00 = math.dot(v0, v0);
            float d01 = math.dot(v0, v1);
            float d11 = math.dot(v1, v1);
            float d20 = math.dot(v2, v0);
            float d21 = math.dot(v2, v1);
            float denom = d00 * d11 - d01 * d01;
            float v = (d11 * d20 - d01 * d21) / denom;
            float w = (d00 * d21 - d01 * d20) / denom;
            float u = 1f - v - w;
            return new float3(u, v, w);
        }

        [BurstCompile]
        public void AddMatrixReplace(int r, int b, NativeArray<float> syMatrix)
        {
            syMatrix[r] = syMatrix[r] + syMatrix[b];
            syMatrix[r + 1] = syMatrix[r + 1] + syMatrix[b + 1];
            syMatrix[r + 2] = syMatrix[r + 2] + syMatrix[b + 2];
            syMatrix[r + 3] = syMatrix[r + 3] + syMatrix[b + 3];
            syMatrix[r + 4] = syMatrix[r + 4] + syMatrix[b + 4];
            syMatrix[r + 5] = syMatrix[r + 5] + syMatrix[b + 5];
            syMatrix[r + 6] = syMatrix[r + 6] + syMatrix[b + 6];
            syMatrix[r + 7] = syMatrix[r + 7] + syMatrix[b + 7];
            syMatrix[r + 8] = syMatrix[r + 8] + syMatrix[b + 8];
            syMatrix[r + 9] = syMatrix[r + 9] + syMatrix[b + 9];
        }
        [BurstCompile]
        public void AddMatrixReplace(int r, NativeArray<float> syMatrix, NativeArray<float> tempMatrix)
        {
            syMatrix[r] = syMatrix[r] + tempMatrix[0];
            syMatrix[r + 1] = syMatrix[r + 1] + tempMatrix[1];
            syMatrix[r + 2] = syMatrix[r + 2] + tempMatrix[2];
            syMatrix[r + 3] = syMatrix[r + 3] + tempMatrix[3];
            syMatrix[r + 4] = syMatrix[r + 4] + tempMatrix[4];
            syMatrix[r + 5] = syMatrix[r + 5] + tempMatrix[5];
            syMatrix[r + 6] = syMatrix[r + 6] + tempMatrix[6];
            syMatrix[r + 7] = syMatrix[r + 7] + tempMatrix[7];
            syMatrix[r + 8] = syMatrix[r + 8] + tempMatrix[8];
            syMatrix[r + 9] = syMatrix[r + 9] + tempMatrix[9];
        }
        [BurstCompile]
        public void AssignTempMatrix(float a, float b, float c, float d, NativeArray<float> tempMatrix)
        {
            tempMatrix[0] = a * a;
            tempMatrix[1] = a * b;
            tempMatrix[2] = a * c;
            tempMatrix[3] = a * d;
            tempMatrix[4] = b * b;
            tempMatrix[5] = b * c;
            tempMatrix[6] = b * d;
            tempMatrix[7] = c * c;
            tempMatrix[8] = c * d;
            tempMatrix[9] = d * d;
        }
        [BurstCompile]
        public void AssignTempMatrixByAdd(int a, int b, NativeArray<float> syMatrix, NativeArray<float> tempMatrix)
        {
            tempMatrix[0] = syMatrix[a] + syMatrix[b];
            tempMatrix[1] = syMatrix[a + 1] + syMatrix[b + 1];
            tempMatrix[2] = syMatrix[a + 2] + syMatrix[b + 2];
            tempMatrix[3] = syMatrix[a + 3] + syMatrix[b + 3];
            tempMatrix[4] = syMatrix[a + 4] + syMatrix[b + 4];
            tempMatrix[5] = syMatrix[a + 5] + syMatrix[b + 5];
            tempMatrix[6] = syMatrix[a + 6] + syMatrix[b + 6];
            tempMatrix[7] = syMatrix[a + 7] + syMatrix[b + 7];
            tempMatrix[8] = syMatrix[a + 8] + syMatrix[b + 8];
            tempMatrix[9] = syMatrix[a + 9] + syMatrix[b + 9];
        }

        public float syGetFromVert(int vindex, int i, NativeArray<float> syMatrix)
        {
            return syMatrix[vindex * 10 + i];
        }

        public int NewSymmetricMatrix(int vindex)
        {
            int s = vindex * 10;
            return s;
        }
        [BurstCompile]
        public int NewSymmetricMatrix(int vindex, float a, float b, float c, float d, NativeArray<float> syMatrix)
        {
            int s = vindex * 10;
            syMatrix[s] = a * a;
            syMatrix[s + 1] = a * b;
            syMatrix[s + 2] = a * c;
            syMatrix[s + 3] = a * d;
            syMatrix[s + 4] = b * b;
            syMatrix[s + 5] = b * c;
            syMatrix[s + 6] = b * d;
            syMatrix[s + 7] = c * c;
            syMatrix[s + 8] = c * d;
            syMatrix[s + 9] = d * d;
            return s;
        }
        [BurstCompile]
        public float Determinant1(NativeArray<float> tempMatrix)
        {
            float det =
                tempMatrix[0] * tempMatrix[4] * tempMatrix[7] +
                tempMatrix[2] * tempMatrix[1] * tempMatrix[5] +
                tempMatrix[1] * tempMatrix[5] * tempMatrix[2] -
                tempMatrix[2] * tempMatrix[4] * tempMatrix[2] -
                tempMatrix[0] * tempMatrix[5] * tempMatrix[5] -
                tempMatrix[1] * tempMatrix[1] * tempMatrix[7];
            return det;
        }
        [BurstCompile]
        public float Determinant2(NativeArray<float> tempMatrix)
        {
            float det =
                tempMatrix[1] * tempMatrix[5] * tempMatrix[8] +
                tempMatrix[3] * tempMatrix[4] * tempMatrix[7] +
                tempMatrix[2] * tempMatrix[6] * tempMatrix[5] -
                tempMatrix[3] * tempMatrix[5] * tempMatrix[5] -
                tempMatrix[1] * tempMatrix[6] * tempMatrix[7] -
                tempMatrix[2] * tempMatrix[4] * tempMatrix[8];
            return det;
        }
        [BurstCompile]
        public float Determinant3(NativeArray<float> tempMatrix)
        {
            float det =
                tempMatrix[0] * tempMatrix[5] * tempMatrix[8] +
                tempMatrix[3] * tempMatrix[1] * tempMatrix[7] +
                tempMatrix[2] * tempMatrix[6] * tempMatrix[2] -
                tempMatrix[3] * tempMatrix[5] * tempMatrix[2] -
                tempMatrix[0] * tempMatrix[6] * tempMatrix[7] -
                tempMatrix[2] * tempMatrix[1] * tempMatrix[8];
            return det;
        }
        [BurstCompile]
        public float Determinant4(NativeArray<float> tempMatrix)
        {
            float det =
                tempMatrix[0] * tempMatrix[4] * tempMatrix[8] +
                tempMatrix[3] * tempMatrix[1] * tempMatrix[5] +
                tempMatrix[1] * tempMatrix[6] * tempMatrix[2] -
                tempMatrix[3] * tempMatrix[4] * tempMatrix[2] -
                tempMatrix[0] * tempMatrix[6] * tempMatrix[5] -
                tempMatrix[1] * tempMatrix[1] * tempMatrix[8];
            return det;
        }
    }
}
3 Likes

Try to tweak the FloatPrecision if you need more precise math.
https://docs.unity3d.com/Packages/com.unity.burst@1.0/api/Unity.Burst.FloatPrecision.html

Appreciate your response, unfortunately I have tried all the float precision options and also all combinations of float mode options. None make any difference to the output.

Thanks for the report, we will have a look

Small update - just to confirm I can reproduce your Burst-only issue and am looking at it!

1 Like

Ok after a few false starts - I think I know which line is going wrong:

(-1.0f / det * Determinant2(tempMatrix)),  // vx = A41/det(q_delta)

If I change this line to:

(-1.0f / (det * Determinant2(tempMatrix))),  // vx = A41/det(q_delta)

EG. do the multiplication before the division, I don’t get the awful mesh degeneration. However the mesh with burst is much less simplified and cannot be simplified further if you run the SimpJob test again.

Can I just confirm that you really want the -1.0f / det to occur before the multiplication?

I’m running out of easy ideas, but it could be that Determinant2 is somehow wrong with Burst, or the division then multiplication is wrong, or lastly that once this gets fed into the VertexError function that the values are being combined in the wrong order and some floating-point explosions occur!

Appreciate you looking into this sheredom! I’ll flick you a PM :slight_smile:

So just for the craic I decided to revisit this issue:

  • Downloaded your sample again.
  • Updated Burst to 1.2.0 preview.9 (where I fixed some floating-point isms).
  • I don’t see the deformation anymore!

So I think this might be fixed by using the latest package :slight_smile:

2 Likes