Search Unity

Animation C# jobs in 2018.2a5

Discussion in 'Animation' started by Mecanim-Dev, Apr 4, 2018.

  1. Baste

    Baste

    Joined:
    Jan 24, 2013
    Posts:
    6,338
    This was happening in a single test scene with no interfering scripts. The model in question had a single AnimationMixerPlayable playing some animations, and that playable was the input to the AnimationScriptPlayable that was playing the animation.

    In other words, root motion was off, but I wasn't moving the root transform. I'll try to create a minimal repro.
     
  2. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    the question is: does your root transform was spawn at identity or at another position/rotation?
     
  3. lifeFo

    lifeFo

    Joined:
    Apr 14, 2017
    Posts:
    15
    I use for loop to sample child transform result sample part of sub gameobject,other transform not change.
    IF i use SampleAnimation in update function,all transfrom get sample properly.
    Code (CSharp):
    1. Dictionary<string, List<Matrix4x4>> m4 = new Dictionary<string, List<Matrix4x4>> (14);
    2.         for (int i = 1; i < boneMatrices.GetLength (0) - 1; i++) {
    3.             float t = (float) (i - 1) / (boneMatrices.GetLength (0) - 3);
    4.             clip.SampleAnimation (renderer.gameObject, t);
    5.             for (int j = 0; j < bones.Length; j++) {
    6.                 var tr = bones[j];
    7.                 List<Matrix4x4> samebone;
    8.                 var cmt = rootbone.worldToLocalMatrix * tr.localToWorldMatrix * bindPoses[j];
    9.                 if (m4.TryGetValue (tr.name, out samebone)) {
    10.                     if (samebone.Contains (cmt)) {
    11.                         samebone.Add (cmt);
    12.                     } else {
    13.                         samebone.Add (cmt);
    14.                     }
    15.                 } else {
    16.                     m4[tr.name] = new List<Matrix4x4> () { cmt };
    17.                 }
    18.                 boneMatrices[i, j] = cmt;
    19.             }
    20.         }
    sample in for loop

    Code (CSharp):
    1. delt1 += Time.deltaTime;
    2.         var clip = AC[0];
    3.         var mtx = sampledBoneMatrices[0];
    4.         int framid = (int) (clip.frameRate * ((delt1 > 1 ? delt1 = 0 : delt1)));
    5.         float t = (float) (framid - 1) / (mtx.GetLength (0) - 3);
    6.         clip.SampleAnimation (target, t);
    7.  
    8.         NativeArray<Matrix4x4> boneTransforms1 = new NativeArray<Matrix4x4> (mtx.GetLength (1), Allocator.Temp);
    9.         for (int i = 0; i < boneTransforms1.Length; i++) {
    10.             var finalmatrix = rootBone.worldToLocalMatrix * bonestran[i].localToWorldMatrix * BindPoses[i];
    11.             if (!bonesmtx.Contains (finalmatrix)) {
    12.                 Debug.LogError ($"not find {bonestran[i].name}  bones corresponding mtx[framid,i]\n{mtx[framid,i]} fram={framid}");
    13.                 //  boneTransforms1[i] = finalmatrix;
    14.  
    15.             }
    16.             boneTransforms1[i] = mtx[framid, i];
    17.  
    18.         }
    sampled in update function.
    What's wrong with above code?

    right is sampled animation
     
    Last edited: Sep 4, 2019
  4. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    1,011
    @Mecanim-Dev
    Hi. It seems that Unity 2019.3 prevents writing values to TransformSceneHandle. (All of SetXXX methods are marked as deprecated). So is this true that after Unity 2019.3, Animation Job can't change the value of non-animated transform?

    Another question, what TransformSceneHandle's value is, if it is child of animated transform? after animated pose in current frame or previous frame's?

    This behaviors are important to implementing secondary-animation system by using Animation Job.

    In my case, my character has two bone groups, one for body, other for secondary animations (cloth, hair, and body colliders and so on). Some of secondary animation bones are children of animated bone, and others are not children.

    Currently, my secondary animation bones are modified at custom Monobehaviour.LateUpdate() and makes quite big performance degradation. So I investigating whether Animation Job will be solution.

    Thanks.
     
    Last edited: Sep 10, 2019
    awesomedata likes this.
  5. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    TransformSceneHandle was first used to write to transform not under the animator component game object hierarchy.

    We don't allow this anymore because the behaviour is not deterministic. Depending on your configuration and how many animator you have in your scene you can end up in situation where you don't get the same result depending on the instantiation order which is pretty bad and hard to debug especially with pooling.

    You can still use TransformSceneHandle to read data outside of the animator control, but you are not allowed to write to it anymore.

    If the transform is under the animator hierarchy you should use a TransformStreamHandle which should yield the correct result regardless of the instantiation order.
     
    twobob, WendelinReich and awesomedata like this.
  6. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    1,011
    Thanks for detailed reply. How about my second question? When I try to read the value of non-animated transform (but children of animated bone) by using TransformSceneHandle, is it previous frame's value? or current animated value?

    Also, how about that allowing writing value to TransformSceneHandle, as user's responsibility like ECS's NativeDisableParallelForRestriction attribute. If the user exactly know what they do, writing value to scene object might not be any problem. I think that Animation system does not need to be strictly deterministic sometimes (maybe almost?) and disallowing writing value to TransformSceneHandle makes Animation Job be less useful.
     
    Last edited: Sep 12, 2019
  7. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    This is an interesting question. I'd like to know this as well.


    So are you saying you have two skeletons for a single character, or are some bones just not attached to the root bone?

    I've been seriously considering this setup because I want to group certain bones for different purposes too, like secondary animation, facial blendshapes, cloth attachments, weapon attachments, etc., then combine them later with their own separate animations/physics.
    Performance-wise, I can see how this can be a problem without being able to piggy-back off of other transforms.
    I was hoping DOTS-based Modular Animation Rigging would solve this issue... I just don't know how well it will work with what I have in mind. Things like Blendshapes and Cloth or piggy-backing off the transform of separate joints like it seems you are trying to do sounds like essentially the thing I'm trying to do, but in-editor and modular...


    Any idea on the efficiency (and feasibility) of the modular rigging workflow I've proposed (either now or in the near future), @Mecanim-Dev ?
     
  8. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    1,011
    In my case, I have base skeleton + some additional bones, which is dynamically combined in runtime (from character customization, weapon and so on). ex) BaseBoneRoot>BaseBoneHead>DynamicBoneHair
    Animator components only animates base skeleton and additional bones are only attached to it.

    Secondary animation is generated in Monobehaviour.LateUpdate() and some of ECS system. But Monobehaviour part is single threaded and slow, ECS part is quite fast but lack of character (animator) wise control for LOD system. So I'm investigating whether Animation Job can be solution (animator-wise controllable and jobified+burst performance).
     
  9. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    It always the transform value at the beginning of the animation update loop. Most of the time it the previous frame value, but if you have a system that change a transform value before the animation update loop it should be picked up by the TransformSceneHandle.
     
  10. condotelvietnam

    condotelvietnam

    Joined:
    Sep 17, 2019
    Posts:
    1
    The tears flow endlessly

    In other news. Is it better to create duplicate mirrored clips or have it done at runtime ala mecanim?
     
  11. markgrossnickle

    markgrossnickle

    Joined:
    Aug 21, 2017
    Posts:
    32
    What is the current state of animations? We are starting to move a project over to ECS. Project should be out early 2020.

    We are currently using SkinnedMesh renderers and an animator for a 3D character. Should we continue doing that and have entities link to a gameObject/monobehaviors in a hybrid approach or is the github repro in a state we should try that first and go full ecs?
     
  12. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    I really want to know this too...

    Something tells me it's still very early despite being on hold for so long... I'm desperate for an animation update. D:
     
    markgrossnickle likes this.
  13. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Hi guys,

    yes it still early, we just completed the Unite demo Dots Shooter with the new com.unity.animation package for DOTS.

    The package is suppose to ship as preview for 2019.3 with Dots Shooter but there is still a lot to do to get to the production level quality.
     
  14. Onigiri

    Onigiri

    Joined:
    Aug 10, 2014
    Posts:
    486
    is this will be similar to mecanim node editor or code only?
     
  15. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Right now it code only, the goal for dots shooter was to reimplement the tech we had for FPS sample but in dots.
    So it graph base like Playable but completly open because you have access to the source code.

    The package currently only support Character animation, we do write the animation result to the character skin mesh matrices diretctly

    Right now we do support the following graph node:
    Mixer
    LayerMixer with override layer, additive layer, and masking.
    Generic Retargeting <---- Yes that right generic
    1d BlendTree
    2d Simple Directionnal BlendTree
    Two bone IK

    Also we do have:
    Animation curve evaluator: You can create a curve at runtime to drive any single property.
    Bone Rendering for debug.
    AnimationStream like API <---if you did create some custom AnimationJobs it pretty easy to port them to this new tech.
    Support for TRS animation curve and custom float curve.

    We are currently working on
    Root motion
    Animation Runtime Rigging
     
    ekakiya, awesomedata, JesOb and 2 others like this.
  16. Kichang-Kim

    Kichang-Kim

    Joined:
    Oct 19, 2010
    Posts:
    1,011
    @Mecanim-Dev Hi, I have some questions for clarification of Animation C# Jobs and Animation Rigging.

    1. What exactly transforms which TransformStreamHandle can handle? Children of Animator's transform? How about runtime-generated transform?

    2. What is different between TransformXXXHandle and ReadWrite/ReadOnlyTransformHandle in rigging package?. It seems that both handles can be used in AnimationJob.

    3. How to turn-off custom Animation Job for Animator? especially, for Animation LOD. I want to disable to my custom Animation Job of distant character for saving computation power, but I can't find any API for disable Animation Job temporarily. Should I set new job data which has immediately return method?

    Thanks.
     
  17. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    Right, Children of Animator transform.
    If you create some transform at runtime and you try to Bind them with a TransformStreamHandle, the animator will generate the missing binding, so it should works.
    But keep in mind that there is a cost to do that. Everytime that your animator will have to rebind the animation it will take a few more cpu cycle to do the same job.
    So keep an eyes on profiler marker "Animator.SetupAvatarDataSet" and make sure that it doesn't take too much time.

    Right, it handle both a TransformStreamHandle and a TransformSceneHandle, now the question is why?
    When you work with IK rig you often bake the resulting animation to either the FK or the IK.
    So in this case an IK effector can either be animated so driven by a TransformStreamHandle or manipulated by the user to set a new IK goal in which case it need to be a TransformSceneHandle.


    Unfortunately there is no built-in support for LOD in the PlayableGraph. One thing you can do is change the topology of the graph based on your LOD
     
    Kichang-Kim likes this.
  18. ahmidou_mpc

    ahmidou_mpc

    Joined:
    Aug 13, 2019
    Posts:
    9
    It seems the AnimationJob system only single threaded in 2019.2.6, is it?
     
  19. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    Thanks for the updates!

    So how might one do that topology change easily? -- Any suggested method for handling this?
    Also, is this planned to be handled automatically in the future?
     
  20. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    it shouldn't, maybe there is a new regression.

    Can you log a bug?
     
  21. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    There is no many way to handle that, the playable API allow you to connect and disconnect playable, son one thing you can do is tag your playable with a LOD level and when you need to change the LOD level you also need to traverse the graph and disconnect playable that doesn't meet the LOD level requirement.

    For playable no, we don't have any LOD support for animation. So I don't think we will ever support LOD for Animation and Animator component.

    But for DOTS animation we are currently looking at different way to handle LOD.
    Either by reducing the complexity of the animation rig for different LOD, think less bone for low level LOD.
    And also by reducing the complexity of the graph, you don't need foot ik correction on character that are too far from the camera, or you may not need all the procedural node to animate props.
     
    awesomedata likes this.
  22. ahmidou_mpc

    ahmidou_mpc

    Joined:
    Aug 13, 2019
    Posts:
    9
    I'm seeing the same behavior in 2019.3b4.
    I'll try to make a simple repro and log a bug this weekend
     
  23. Unityraptor81

    Unityraptor81

    Joined:
    Nov 28, 2016
    Posts:
    29
    Hey All,

    Is there a way to "read" an animation clip bone transform properties without having an animator controller ?

    I'm working on active "physical" ragdolls and right now the only solution is to duplicate skeleton hierachy with a first invisible animated skeleton and a second active one where we physically target the transform of the first.

    This is really perf hungry. Having a simple solution to just read a clip transforms to match them would be nice, and doing this in a job would be even better.

    Thanks,

    Jay
     
  24. zarex7

    zarex7

    Joined:
    Jan 25, 2016
    Posts:
    21
    Hello all!

    Are it possible to use in Humanoid something like

    Anim.SetMuscle("Jaw")=10; ?

    to just set value to muscles by C# script?
     
  25. zarex7

    zarex7

    Joined:
    Jan 25, 2016
    Posts:
    21
    So, I found some solution... but need help to debug. So this code should open-close model's mouth every second:

    Code (CSharp):
    1. using System.Collections;
    2. using System.Collections.Generic;
    3. using UnityEngine;
    4.  
    5. public class HeadRotatorMuscle : MonoBehaviour
    6. {
    7.  
    8.     //   Human muscle stuff
    9.     HumanPoseHandler humanPoseHandler;
    10.     HumanPose humanPose;
    11.     Animator anim;
    12.  
    13.     // Indexes to muscles
    14.     // 9: Neck Nod Down-Up min: -40 max: 40
    15.     // 10: Neck Tilt Left-Right min: -40 max: 40
    16.     // 11: Neck Turn Left-Right min: -40 max: 40
    17.     // 12: Head Nod Down-Up min: -40 max: 40
    18.     // 13: Head Tilt Left-Right min: -40 max: 40
    19.     // 14: Head Turn Left-Right min: -40 max: 40
    20.  
    21.  
    22.     //   Bone stuff
    23.     Transform head;
    24.     Transform jaw;
    25.     Transform leye;
    26.     Transform reye;
    27.  
    28.     // Muscle name and index lookup (See in Debug Log)
    29.     void LookUpMuscleIndex()
    30.     {
    31.         string[] muscleName = HumanTrait.MuscleName;
    32.         int i = 0;
    33.         while (i < HumanTrait.MuscleCount)
    34.         {
    35.             Debug.Log(i + ": " + muscleName[i] +
    36.                 " min: " + HumanTrait.GetMuscleDefaultMin(i) + " max: " + HumanTrait.GetMuscleDefaultMax(i));
    37.             i++;
    38.         }
    39.     }
    40.  
    41.     // Set character in fetus position
    42.     void ResetMuscles()
    43.     {
    44.         // reset all muscles to 0
    45.         for (int i = 0; i < humanPose.muscles.Length; i++)
    46.         {
    47.             //Debug.Log (humanPose.muscles [i]);
    48.         humanPose.muscles[i] = 0;
    49.         }
    50.     }
    51.  
    52.  
    53.     //   !!! Human Pose approach !!!
    54.     void Start()
    55.     {
    56.         // https://forum.unity.com/threads/humanposehandler.430354/
    57.  
    58.         // get attached Animator controller
    59.         anim = GetComponent<Animator>();
    60.  
    61.         // run this if you want the indexes to muscles on your character
    62.         LookUpMuscleIndex();
    63.  
    64.         // TODO keeping body above plane
    65.         //Vector3 current_position = transform.position;
    66.  
    67.         // get human pose handler
    68.         humanPoseHandler = new HumanPoseHandler(anim.avatar, transform);
    69.         // get human pose
    70.         humanPose = new HumanPose();
    71.  
    72.         // TODO keeping body above plane
    73.         //humanPose.bodyPosition = current_position;
    74.  
    75.         // reference pose to pose handler
    76.         humanPoseHandler.GetHumanPose(ref humanPose);
    77.  
    78.         // set a specific musle; 9: Neck Nod Down-Up
    79.         //humanPose.muscles[9] = -20f;
    80.         //Debug.Log(humanPose.muscles[9]);
    81.  
    82.         humanPose.muscles[19] = -10f;  // -10 ... 10
    83.         Debug.Log(humanPose.muscles[19]);
    84.  
    85.         // use pose information to actually set the pose; doesn't work so far
    86.         humanPoseHandler.SetHumanPose(ref humanPose);
    87.  
    88.         StartCoroutine(ExampleEvery3sec());
    89.     }
    90.  
    91.     // Update is called once per frame
    92.     void Update()
    93.     {
    94.         //humanPoseHandler.SetHumanPose(ref humanPose);
    95.     }
    96.  
    97.     IEnumerator ExampleEvery3sec()
    98.     {
    99.         //print(Time.time);
    100.         yield return new WaitForSeconds(1);
    101.         //print(Time.time);
    102.  
    103.         humanPose.muscles[19] = Random.Range(-10.0f, 10.0f);  // -10 ... 10
    104.         Debug.Log(humanPose.muscles[19]);
    105.         humanPoseHandler.SetHumanPose(ref humanPose);
    106.  
    107.     }
    108.  
    109. }
     
  26. ahmidou_mpc

    ahmidou_mpc

    Joined:
    Aug 13, 2019
    Posts:
    9
    before logging a but I just want to be sure that there's one, here's a profiler screenshot of the Animation Rigging workshop' ninja rig.
    Should it be parallelized or is it the expected behavior?
    upload_2019-10-2_19-47-16.png
     
  27. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    it should be parallelized, also I don't understand why you have a deep level callstack like this, look like you are invoking a bunch of evaluation directly from your animation jobs.

    are you using
    AnimationStream.GetInputStream()
    to invoke manually the evaluation of all the playable graph?
     
  28. ahmidou_mpc

    ahmidou_mpc

    Joined:
    Aug 13, 2019
    Posts:
    9
    All you see in that screen-shot is Animation-Rigging package related, I just opened the "SampleSceneNinja.unity" scene from that project:
    https://github.com/Unity-Technologies/animation-rigging-workshop-siggraph2019
    afaik
    AnimationStream.GetInputStream()
    is not used at all.
     
  29. ahmidou

    ahmidou

    Joined:
    Sep 17, 2012
    Posts:
    87
    @Mecanim-Dev I finally got some time to look at it again.
    It seems Animation jobs are parallelized per graph, not per job.
    If I take the Animation-Rigging 2boneIK test scene where there's 2 legs, it's running on a single thread,
    but if I duplicate the rig hierarchy, it is then distributed.
    Is it the intended behavior? If so this should be documented as it is unclear.
     
    Last edited: Oct 10, 2019
  30. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    you're right and it has been like that since we implemented mutlithreading in the animation system in 2011.

    that a look at this documentation page, you will see which animation steps are multithreaded

    https://docs.unity3d.com/Manual/ExecutionOrder.html

    Basically it StateMachineUpdate, ProcessGraph, ProcessAnimation and Write Transforms.

    We do schedule jobs per animator, not per playable in the graph, the context switch cost is too high if you start to schedule at this level of granularity.

    Also it up to the scheduler to choose how to distribute the jobs. There is a few sync point in the animation system and if the main thread is wainting on a sync point and there still jobs to execute, the main thread will start to execute jobs.

    As you add more rigged character you should see the jobs starting to distribute more evenly
     
  31. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    That's a terrible place for documentation about the underlying Animation system... I really think it deserves its own section, or at least to be mentioned in the overview on animation as a footnote someplace.
     
  32. ahmidou

    ahmidou

    Joined:
    Sep 17, 2012
    Posts:
    87
    That's good to know, thanks for the explanation
    The documentation page you pointed isn't really saying that much thought. The only mention is:
     
  33. Unityraptor81

    Unityraptor81

    Joined:
    Nov 28, 2016
    Posts:
    29
    @Mecanim-Dev Any tips about this one ?
     
  34. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    You didn't see the legend?
    legend.png
     
  35. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    yes it can be done but the setup is awful, you need a animation jobs that can read all the transforms from the scene value and store the animation pose into this job so basically a job with an array of TransformSceneHandle and an array of positions and rotations

    1 . you need to deactivate the animator component when you are going to ragdoll to let the physics run the simulation.
    2. when you are ready to blend from ragdoll to animation, you need to enable back the animator and also the animation job that read all the transforms position + rotation on this frame.
    3. for a few frame you blend the result of this physics pose with your animation.
     
  36. Partel-Lang

    Partel-Lang

    Joined:
    Jan 2, 2013
    Posts:
    2,556
    But as soon as you enabled the Animator, would it not just break the ragdoll simulation immediately?

    Even with Physics.autoSyncTransforms disabled, synchronization still happens prior to the physics simulation step.
    All we need here is bool Rigidbody.syncTransform that would be defaulted to enabled, but could be disabled by script for all the character rigidbodies to completely decouple the Transform and Rigidbody components.

    That way we could leave our Animator enabled at all times, have ragdolls manipulated via joints and AddForce only and have an animation job take care of the blending between ragdoll and animated pose (Would probably need something like RigidbodySceneHandle to read Rigidbody.position and rotation). That would enable us also to do partial blending, like having the arms dangle around with the rest of the body animated and all kinds of other cool stuff.

    Best,
    Pärtel
     
  37. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    yes you're right, hence why I said that the setup is awful, it the only way I managed to make it work without changing the physics code.

    You can already read the value of Rigidbody.position and rotation with a PropertySceneHandle of type float. You need three handle for .x, .y, .z

    As for a better integration with physics, I've been pushing on this since 2016 but there is always something more important on the road map and I do not control the road map so ....
     
  38. Unityraptor81

    Unityraptor81

    Joined:
    Nov 28, 2016
    Posts:
    29
  39. thebarryman

    thebarryman

    Joined:
    Nov 22, 2012
    Posts:
    130
    Any reason why an empty IAnimationJob with the BurstCompile attribute doesn't burst compile? Using 2018.4.11
     
  40. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    Is this genuinely not going to be a thing? -- I was thinking the DOTS physics and DOTS animation were going to be together?

    I suppose what you're talking about isn't DOTS though...
     
  41. Mecanim-Dev

    Mecanim-Dev

    Joined:
    Nov 26, 2012
    Posts:
    1,675
    right, I was talking about the animator component and physx.
     
    awesomedata likes this.
  42. MadeFromPolygons

    MadeFromPolygons

    Joined:
    Oct 5, 2013
    Posts:
    3,982
    I think based on all of this I will probably just go with FinalIK, its sounding like this system is not really usable for active ragdoll purposes which is what I need.

    Can anyone confirm whether using FinalIK would be a better option over this?
     
  43. Partel-Lang

    Partel-Lang

    Joined:
    Jan 2, 2013
    Posts:
    2,556
    Hey,
    Just to clarify, Final IK is an inverse kinematics package, doesn't deal with physics. The stuff we talked about earlier is what PuppetMaster does and yes, it can do all that stuff. I replied here hoping it would be possible some day to get rid of the dual rig system (managing animated character and ragdoll as separate hierarchies) that is has to use because of the Unity limitations discussed above.
     
  44. CptKen

    CptKen

    Joined:
    May 30, 2017
    Posts:
    216
    I implemented 'Inertialization' (aka 'Inertial Blending') yesterday with an 'AnimationScriptPlayable' and an animation job. Basically there is no 'true' blending, the animation switches from one clip to the next instantly and the transition is generated as a post process.

    UPDATE: In my original post I was having some severe performance issues. Turns out I was using Unity 2018.4. Probably because that is the base version for my motion matching asset. I upgraded my test project to 2019.2.9f1 and the animation Job is now Burst Compiling and running on a worker thread. Down to 0.08 - 0.1ms (from 0.5ms previously). Still seems a bit slow for what it is but It's at least down to a reasonable level where I can probably optimise the rest of the way myself.

    I still feel like the TransformStreamHandle operations are very slow though.

    InertialBlending_Test.gif
     

    Attached Files:

    Last edited: Nov 3, 2019
    SenseEater likes this.
  45. CptKen

    CptKen

    Joined:
    May 30, 2017
    Posts:
    216
    Ok so after some additional profiling I'm not sure I will be able to make this feasible. All the overhead seems to be in the TransformStreamHandles and my own calculations are taking up a very minute amount of time now that I have burst working on the Animation Job.

    Here's what I found. Calling TransformStreamHandles.GetLocalPosition(stream) and TransformStreamHandles.SetLocalPosition(stream, pos); is incredibly slow. For each transform I set the local rotation and position once. This 'setting' of positions and rotations alone takes up more than half my job processing time... Which is crazy because I'm doing a hell of a lot of calculations. If you combine that with a few 'Getters', simply using TransformStreamHandle / TransformSceneHandle is taking up more than 2/3rds of my job processing time.

    It gets worse though. If you manually set a value on a TransformStreamHandle, there will be a big overhead after the job is completed. My guess is that the changes in the job are being applied on the main thread. Fair enough, but it's taking up a whopping 0.034ms in addition which is totally out of my control.



    Since there is no alternative to TramsformStreamHandle to modify the transforms in the animation stream, I'm stuck with a 0.08 - 0.12ms overhead just to make my custom blend, where my own calculations for the blend (which are in my control) take only 0.01ms.

    At this point I think I could probably achieve this faster by using a regular IJobParralelBatch to run the calculations on the transforms in batches and then apply the post processing to the transforms in the late update manually :(.

    How is this done so fast with 'AnimationMixerPlayable'? I'm assuming it's done with a lower level internal API that we don't have access to. If not, it would be good to know how because I've implemented this with a similar process to the Animation Job samples and it's just too slow.

    Edit: I'm aware that everything is moving towards DOTS land and the DOTS animation system will be coming out soon. With that in mind I'm not expecting this to get much attention. I'm hoping that the DOTS animation system is more performant in this regard and allows lower level access. I know a lot of abstraction is implemented for safety but I tend to spend a lot of time trying to get around rigid safety controls for the sake of perf. Would be nice to have a way to be closer to the metal if we choose to.
     
    Last edited: Nov 3, 2019
  46. Partel-Lang

    Partel-Lang

    Joined:
    Jan 2, 2013
    Posts:
    2,556
    After a lot of testing and experimenting I pretty much gave up on Animation Jobs. Everything I tried just ended up considerably slower than doing the same thing in LateUpdate, even if solved on hundreds of instances (which you probably don't need anyway unless you work on something like Total War). Even a job that does nothing is slower than a small MonoBehaviour solver like 2-bone IK. Animation and especially IK are terrible candidates for multi-threading by nature because they need to work on hierarchical data structures and be solved in specific linear order. DOTS animation is gonna be faster, but I think not nearly as much as you've seen in some of the DOTS tech demos that are about moving thousands of independent objects.

    About Inertialization, you should be able to do that with LateUpdate too. Get rid of transitions, use Animator.Play to call the next state and interpolate the pose and velocities in LateUpdate. By the way, we would get the same visual quality if Mecanim just had an option for smooth transition interpolation (that would ensure continuity of inertia without even having to calculate any velocities), but if it's not on the roadmap, it's not on the roadmap.
     
    FlavioIT, nostro66 and CptKen like this.
  47. CptKen

    CptKen

    Joined:
    May 30, 2017
    Posts:
    216
    Yes this definitely seems to be the case. I could definitely do this faster by doing my inertialization calculations with IJobParralelBatch and then applying them in the hierarchical order in LateUpdate. It just kinda made me sad because I'm not really sure what the purpose of AnimationJobs is at this point.

    Regarding mecanim and non linear transitions, that's not really an issue for me since my animation solution is completely custom. I already use a sin based transition and you're right, it's far superior to linear blends. What I want out of the inertialization is Performance:
    • Only 1 animation clip evaluated at any one time (With Motion Matching I could be evaluating 8 in a single frame)
    • I can avoid creating and destroying playables at run time (no other way around this with motion matching)
    • I can do away with my MM blending logic. I no longer have to keep track of up to 8 channels and their life cycle. I just need to know the current pose the previous pose and the target pose.
    The irony is that Unity won't allow me to do this in a performant enough manner so that I could realise these benefits.

    I should try the late update method. I'm interested to see how fast I could make it.
     
    Last edited: Nov 3, 2019
    SenseEater likes this.
  48. awesomedata

    awesomedata

    Joined:
    Oct 8, 2014
    Posts:
    1,419
    I'm with you on that. Not too sure what the deal is officially, but from what I've gathered, perhaps it was mainly to create a semi-performant version of modular animation rigging so that it could eventually be converted to DOTS?

    Anyone know the official reason? -- Seems terrible if performance is worse than rolling your own solution...
     
  49. fherbst

    fherbst

    Joined:
    Jun 24, 2012
    Posts:
    802
    Hey there, I'm having an issue with AnimationJobs. How can I make TransformStreamHandle.GetPosition / GetRotation aware of transform changes to other bones up the hierarchy? Documentation says these are supposed to be "world position" but in fact they seem to be "world positions if nothing in the hierarchy would have changed".

    I tried making the animation stream "aware" of the transforms by using another Job to write the transform that doesn't seem to be synced, and I tried using the "RigTransform" component, both of which work (animations down the chain are transformed) but the result of GetPosition / GetRotation is still wrong.

    You can see the issue here (I already modified the Damping script to work properly when not parented to the Animator, and it works great, the issue is that the pink dots - direct output of TransformStreamHandle.GetPosition - are in the "untransformed" position as if "GameObject" hadn't moved).
    20191106-002253.gif
     
  50. CptKen

    CptKen

    Joined:
    May 30, 2017
    Posts:
    216
    The Documentation is correct. A world position is just that, a position in the world that is unconstrained by parents. You should be working in local space with animation Jobs most of the time. If you want to update them using world positions and have them be aware of their parents you need to sort your handles in hierarchical order and then progressively transform them. However, you're better off just calculating local transforms and applying them to the handles in local space.
     
    Last edited: Nov 6, 2019