Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
ObiSkinnedCloth.SetTargetSkin Jobification
#1
posting this here so it can be verified and hopefully merged into the project (unless supersed already by better code):
Modifications in ObiSkinnedCloth.cs:
Code:
//Top of file:
using Unity.Jobs;
using Unity.Collections;
using Unity.Burst;
using Unity.Mathematics;

...

//replaces existing sortedPoints/Normals
private NativeArray<Vector4> sortedPoints;
private NativeArray<Vector4> sortedNormals;
private NativeArray<int> actorIndices;

...

//Changing LoadBlueprint to init native arrays
public override void LoadBlueprint(ObiSolver solver)
{
    base.LoadBlueprint(solver);
    SetupRuntimeConstraints();

    var skinConstraints = GetConstraintsByType(Oni.ConstraintType.Skin) as ObiConstraints<ObiSkinConstraintsBatch>;
    if (skinConstraints != null && skinConstraints.GetBatchCount() > 0)
    {
        var batch = skinConstraints.batches[0] as ObiSkinConstraintsBatch;
        if(sortedPoints.IsCreated)
        {
            sortedPoints.Dispose();
        }
        sortedPoints = new NativeArray<Vector4>(batch.constraintCount, Allocator.Persistent);

        if(sortedNormals.IsCreated)
        {
            sortedNormals.Dispose();
        }
        sortedNormals = new NativeArray<Vector4>(batch.constraintCount, Allocator.Persistent);

        if(actorIndices.IsCreated)
        {
            actorIndices.Dispose();
        }
        actorIndices = new NativeArray<int>(batch.constraintCount, Allocator.Persistent);
    }
}

...

//Adding on destroy to cleanup native arrays
private void OnDestroy()
{
    if(sortedPoints.IsCreated)
    {
        sortedPoints.Dispose();
    }

    if(sortedNormals.IsCreated)
    {
        sortedNormals.Dispose();
    }

    if(actorIndices.IsCreated)
    {
        actorIndices.Dispose();
    }
}


private void SetTargetSkin()
{
    using (m_SetTargetSkinPerfMarker.Auto())
    {

        var skinConstraints = GetConstraintsByType(Oni.ConstraintType.Skin) as ObiConstraints<ObiSkinConstraintsBatch>;
        var batch = skinConstraints.batches[0] as ObiSkinConstraintsBatch;

        using (m_SortSkinInputsPerfMarker.Auto())
        {
            int pointCount = bakedVertices.Count;
            for (int i = 0; i < pointCount; ++i)
            {
                int welded = m_SkinnedClothBlueprint.topology.rawToWelded[i];
                sortedPoints[welded] = bakedVertices[i];
                sortedNormals[welded] = bakedNormals[i];
            }
        }

        using (m_SetSkinInputsPerfMarker.Auto())
        {
            for(int i = 0; i < batch.activeConstraintCount; ++i)
            {
                int solverIndex = batch.particleIndices[i];
                actorIndices[i] = solver.particleToActor[solverIndex].indexInActor;
            }

            setSkinInputsJob.approxValue = Mathf.Epsilon * 8f;
            setSkinInputsJob.skinToSolver = actorLocalToSolverMatrix;

            setSkinInputsJob.sortedPoints = sortedPoints;
            setSkinInputsJob.sortedNormals = sortedNormals;

            setSkinInputsJob.particleIndices = batch.particleIndices.AsNativeArray<int>();
            setSkinInputsJob.actorIndices = actorIndices;

            setSkinInputsJob.skinCompliance = batch.skinCompliance.AsNativeArray<float>();
            setSkinInputsJob.skinRadiiBackstop = batch.skinRadiiBackstop.AsNativeArray<float>();

            setSkinInputsJob.skinPoints = batch.skinPoints.AsNativeArray<Vector4>();
            setSkinInputsJob.skinNormals = batch.skinNormals.AsNativeArray<Vector4>();

            setSkinInputsJob.invMasses = solver.invMasses.AsNativeArray<float>();
            setSkinInputsJob.positions = solver.positions.AsNativeArray<Vector4>();

            setSkinInputsJob.Run();
        }
    }
}

SetSkinInputsJob setSkinInputsJob = new SetSkinInputsJob();

[BurstCompile(FloatMode = FloatMode.Deterministic)]
struct SetSkinInputsJob : IJob
{
    public float approxValue;
    public Matrix4x4 skinToSolver;

    [ReadOnly]
    public NativeArray<Vector4> sortedPoints;
    [ReadOnly]
    public NativeArray<Vector4> sortedNormals;

    [ReadOnly]
    public NativeArray<int> particleIndices;
    [ReadOnly]
    public NativeArray<int> actorIndices;

    [ReadOnly]
    public NativeArray<float> skinRadiiBackstop;
    [ReadOnly]
    public NativeArray<float> skinCompliance;

    //[WriteOnly]
    public NativeArray<Vector4> skinPoints;
    [WriteOnly]
    public NativeArray<Vector4> skinNormals;

    [WriteOnly]
    public NativeArray<float> invMasses;
    [WriteOnly]
    public NativeArray<Vector4> positions;

    public void Execute()
    {
        for(int i = 0; i < particleIndices.Length; ++i)
        {
            int solverIndex = particleIndices[i];
            int actorIndex = actorIndices[i];

            skinPoints[i] = skinToSolver.MultiplyPoint3x4(sortedPoints[actorIndex]);
            skinNormals[i] = skinToSolver.MultiplyVector(sortedNormals[actorIndex]);

            // Rigidly transform particles with zero skin radius and zero compliance:                   
            if(math.abs(skinRadiiBackstop[i * 3]) <= approxValue && math.abs(skinCompliance[i]) <= approxValue)
            {
                invMasses[solverIndex] = 0;
                positions[solverIndex] = skinPoints[i];
            }
        }
    }
}
I assume there's some potential optimisation by switching to float4x4s and float4s across the board, but I've had no luck implementing that, and the job is already very fast
You could also reduce some of the job data assignment calls probs.

For me it reduced the task from ~0.03-0.04ms per SkinnedCloth with about 50-80 vertices down to ~0.02-0.025ms
Reply


Messages In This Thread
ObiSkinnedCloth.SetTargetSkin Jobification - by ThoughtMango - 04-06-2021, 03:19 PM