Search Unity

AnimationCurve: GetKey or GetKeysNonAlloc

Discussion in 'Scripting' started by victor_apihtin, Mar 18, 2021.

  1. victor_apihtin

    victor_apihtin

    Joined:
    Mar 2, 2021
    Posts:
    26
    It's just came to me that AnimationCurve does not have public GetKey, but it does have it internally. Neither NonAlloc GetKeys is available. Sure thing, it's not a lot of memory, but if it already exists, why not make it public.

    Now, together with Animator, it makes a list of forever garbaging classes.
     
  2. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    608
    You can use the keys property or just the index operator on the AnimationCurve object itself:
    Code (CSharp):
    1. Keyframe keyFrame = animationCurve[keyFrameIndex];
     
    Last edited: Mar 18, 2021
  3. victor_apihtin

    victor_apihtin

    Joined:
    Mar 2, 2021
    Posts:
    26
    So .keys is making a new array every time you call for it.
    indexer operator is nice, so there is a GetKey after all, thanks
    However, it is read only. It leaves me at missing SetKey now to become garbage-zero, so that's an improvement :)
    My use case is that I want to change all data on the curve, without making garbage.
    So a SetKey would be.. calling RemoveKey(i), and then calling AddKey at the same time point.. which is just weird
     
  4. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    7,820
    It is weird/annoying that Unity keeps this method internal. And getting a fix is likely not going to happen any time soon. Of course you can flag this thread as feedback to hopefully get their attention.

    In the mean time you can create an extension method for your version of 'SetKey' that does what you want it to. Consolidating your "weird" logic into a single call:
    https://docs.microsoft.com/en-us/do...g-guide/classes-and-structs/extension-methods
     
    victor_apihtin likes this.
  5. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    608
    No telling for sure, since it is a native function, but I expect calling RemoveKey is going to create garbage, whether you subsequently call AddKey or not.

    Why does creating garbage concern you?
     
  6. victor_apihtin

    victor_apihtin

    Joined:
    Mar 2, 2021
    Posts:
    26
    Why does it not concern you ? xD
     
  7. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    7,820
    It doesn't concern me until I'm creating so much garbage every frame that it triggers GC frequently and costs me performance issues.

    If I was say accessing the 'keys' property every single frame in a script that gets populated on several GameObjects, then I'd probably look for a way to avoid all that garbage.

    But if it was something that happens on occasion, I wouldn't concern myself with it.

    Garbage happens... all garbage is, is objects you don't need anymore. Unless you held ALL your objects in memory forever (which would be a problem since every new object would take new memory resulting in more and more memory being used over time)... you have to get rid of them sooner or later.

    Example when you change scenes a TON of garbage is created since all those GameObjects in the previous scene are destroyed and their mono/.net side representations in memory are no longer needed and therefore eligible for GC.

    I mean... how often are you editing these AnimationCurves? Why are you editing them?

    This isn't to say you should just ignore GC all together. But rather just write your code how you're used to and if you start noticing issues check the profiler and see what's happening. If it's GC related, find the hot spots that are creating the most garbage and rewrite to not create so much garbage. Then moving forward you will likely remember "oh, this code I have that animates a texture and reads in the entire byte array every frame creates a butt ton of garbage... so I remember in the future if I ever need to do that, write it in a way where I'm not allocating that massive array every frame". You'll over time get a list of "hot spot" code styles that'll adjust your moment coding, but doesn't require large amounts of dev time dedicated to pre-empting garbage since avoiding garbage all together is a near impossibility. And I feel the Unity community by and large has this strange phobia of the garbage collector resulting from a whole lot of FUD that spreads around the forums related to the older runtimes of old versions of Unity (and honestly even back then I seldom had GC issues except in the most extreme cases ala large array allocations every frame).

    And sure the keys array is made up of KeyFrame structs at 28bytes per KeyFrame. So lets say a AnimationCurve in your game is averaging 10 keyframes per curve. That's 280 bytes every time you access it. How often are you doing this? If you're accessing this EVERY frame... weird... what, are you animating an animation curve? What is going on here? But lets just say you are... then cache the array so you're not accessing it every frame! Problem solved.

    ...

    TLDR - over optimization can often lead to more time spent on fiddling with bits and less time... making games.

    To summon the @Kurt-Dekker, a user who is a big proponent of: if it works, don't fix it, keep moving forward and making good games.
     
    Last edited: Mar 19, 2021
  8. victor_apihtin

    victor_apihtin

    Joined:
    Mar 2, 2021
    Posts:
    26
    You can't cache an animation curve that is inside a MinMax struct, that is inside a Velocities struct field, that is inside a particle system module struct, and fetching any of these is going to pop you a new AnimationCurve inside.

    My point being here is not about tolerating garbage, it's about letting the user control when it happens. It is not a lot to wish for :)
     
  9. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    608
    May I ask how long you've been writing code in a managed language, victor_apihtin? The reason I ask is that I teach computer programming and have noticed that newcomers to managed languages tend to become somewhat obsessed with the GC, when the best thing for them to do is ignore it until it becomes a visible performance issue (which it almost never does). When you see your application's memory consumption fluctuating by hundreds of megabytes every few seconds, you'll know you have a GC issue. Until then, you can ignore this and, hard as it may be, just trust in the gods and everything will be okay.
     
    lordofduct likes this.
  10. victor_apihtin

    victor_apihtin

    Joined:
    Mar 2, 2021
    Posts:
    26
    Since we are making it personal, which I am not sure why, we are past the developing stage with our project, and currently are busy with optimising the build for the Nintendo switch platform. As a part of a team full of artists, very often it happens that some of the artists add their bits of coding, and very often it happens it is highly inefficient. I mentioned above particle systems, that is something an artist works with, but also sometimes modifies from scripts, along with other animation systems. Hundreds of objects like that, on a mobile system like switch, eventually causes spikes after CG buildup. And I have to clean this up. Imagine now you are dreaming your switch build to run at 60fps (16ms of budget per frame). How many milliseconds used on garbage collection would you spare off that budget? Considering that not hitting the 16ms budget by only a bit, drops the frame rate to 30 instead of 60, is it something you can close your eyes on?
    Second, I don’t know about you guys, but engineers tend to be perfectionists. There is a reason why Unity keeps adding NonAlloc versions of their vanilla API, and there is a reason behind that they are spending so many resources on the new entire garbage-less DOTS API.
    Anyway, I have my reasons why I don’t want my game making garbage. And this conversation has gone long pass the initial topic. But to answer your question, it has been over 10 years I’ve been coding in Unity. I’m not sure if that helps to validate your point :)
     
  11. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    608
    Okay, just asking. Folks with less experience than you have sometimes benefit from a bit more discussion about how the GC works and why some of their concerns are not always warranted.
     
    victor_apihtin likes this.
  12. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    7,820
    I already agreed that the internal method existing and not being public is stupid... that sucks a lot.

    You just asked why GC doesn't concern us... so I gave you an answer.

    You also have to keep in mind, we only have what information you give us. This is why I asked:
    So, from this post you're saying you're getting the AnimationCurve's off of the MinMaxCurve of either of the 3 Velocity modules of a ParticleSystem object at runtime to then modify said curve?

    Ok... and why can't you cache this? Those structs are really just handles into the body of the ParticleSystem. At the end of the day that AnimationCurve is a member of the ParticleSystem object. Just with a longer access name:
    Code (csharp):
    1. var inheritVelocityAnimationCurve = ps.inheritVelocity.curve.curve;
    That inheritVelocityAnimationCurve is that curve for that ParticleSystem, that's why in the example you can just set its members and the ParticleSystem is updated without having to set it back onto itself.
    https://docs.unity3d.com/ScriptReference/ParticleSystem-inheritVelocity.html
    Code (csharp):
    1.  
    2.        ParticleSystem ps = GetComponent<ParticleSystem>();
    3.        var iv = ps.inheritVelocity;
    4.        iv.enabled = true;
    5.  
    6.         AnimationCurve curve = new AnimationCurve();
    7.        curve.AddKey(0.0f, 1.0f);
    8.        curve.AddKey(1.0f, 0.0f);
    9.        iv.curve = new ParticleSystem.MinMaxCurve(1.0f, curve);
    10.  
    So, sure you could cache it.

    Of course... you can't if it's because you're only accessing that specific ParticleSystem once. Caching it would be a waste of time since caching something used once is pointless. So maybe you're accessing multiple ParticleSystem's that are being created frequently and on creation quickly modifying its curve so as to direct the particle where you want it (like shooting away from the direction the thing that spawned it is moving?)

    To which I refer back to the "we only have the information you give us" thing.

    So...

    With the information I now have. You're done developing your game. You're at this point that you're trying to optimize it for the Switch.

    You've ran the profiler than I assume?

    And you determined it's this modifying of the AnimationCurve of the minmax of the velocity of the ParticleSystem that is causing your biggest allocations of garbage which is leading to GC and resulting in frame stutter?

    Can you maybe show us that specific offending code?

    Maybe we can help come up with a way to optimize it.

    Rather than talking about theory and poo-pooing on Unity for one of their many dumb choices in designing their API (I've spent 10 years doing that now...). Cause otherwise, that's all your Original Post is... just a gripe. It'd be like walking into a building and say "gosh I hate the design of this building!" OK... don't be surprised if the people in the building attempt to defend the building they live in. They didn't know the building killed your gramma or whatever.
     
    Last edited: Mar 20, 2021
  13. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    7,820
    And seriously... griping, nor asking Unity to change things, honestly doesn't get you much.

    I asked 4 years ago for them to uncover a single property that I know to be inherit to the physics engine they use (PhysX), and I added a request back when their request system was in place, and I flagged it feedback when they migrated to the 'feedback' system.
    https://forum.unity.com/threads/req...trary-up-axis-for-charactercontroller.480962/

    So yeah, a passing gripe ain't going to get them to change anything nor fix your problem.
     
  14. victor_apihtin

    victor_apihtin

    Joined:
    Mar 2, 2021
    Posts:
    26
    To be honest, I did make this topic mostly to complaint, out of frustration, and thus did not give a lot detail about my specific issue, and did not expect it to turn out into general GC discussion. I apologize for giving a wrong impression, I ll think twice next time.

    I haven’t tried using MinMax constructor, I’m gonna try that, thanks for the tip.
     
  15. Stevens-R-Miller

    Stevens-R-Miller

    Joined:
    Oct 20, 2017
    Posts:
    608
    Well, thank goodness someone's got that covered. I devote all my griping to their documentation.

    Yeah, we all do that sometimes.
     
    lordofduct likes this.
unityunity