Removing Baked FCurves

Consens

It’s not a good idea to remove the operator outright for 4.0

  • It is still a valid way to shrink the filesize
  • Some users rely on the bake functionality

However it would be good to rename it and update the description/manual to indicate its purpose.
Then a new baking operator is needed, one that provides benefits over the current hacky workflow of baking and unbaking the curve.
Then over the course of the 4.0 cycle we should be able to make the operator fully redundant by optimizing how keys themself are stored.

For the near tearm tasks I’ve added a small todo task to track the progress
#111050: Better Baking in the Graph Editor

Original Post

Blender has the Feature of baking FCurves which converts the BezTriple array into an FPoint array, this is called “Bake FCurve” in the UI (not to be confused with “Bake Action” which adds bezier keys). The baked curve cannot be edited, and it has a datapoint on every frame.

It can be accessed in the Graph Editor under Channel.

The reason given for why this exist is the following

* This is the bare-minimum data required storing motion samples. Should be more efficient
* than using BPoints, which contain a lot of other unnecessary data...

Why remove them

  • Users are confused about that feature. In fact I thought it improved performance while it seems to only save disk space.
  • It is a destructive operation and the resulting data can’t be edited, limiting its usefulness
  • It adds complexity to the code.
  • Subframe data is interpolated linearly so motion blur won’t work correctly.
  • In order to save space it would be better to improve the FCurve storage instead. That way everyone profits automatically

Storage Saved

Assuming a character animation (62 bones) with 6000 frames, keyframes on every frame and euler rotations. That is 3.348.000 keys. With FPoint this would amount to 53.568.000 bytes or 53 megabytes. With BezTriple it is 241.056.000 bytes or 241 megabytes. This is of course a worst case example. Assuming sparse data, having a Bezier Key every 5th frame would be approximately the same size as the baked data.

Playback performance

Test Setup: 6000 frames of animation on a character with 62 bones. A key on every frame on all channels. Unlocked frame rate

There was no discernable performance difference. In both cases the playback was around the 700 frames per second mark.

Proposed Roadmap

4.0: Rename “Bake Curve” operator
4.x: Improve animation storage of Bezier Data
5.0: Remove baked FCurves from code

Waiting for 5.0 to completely remove it from code will allow any use case for this feature to pop up and be addressed. So far I’ve not heard from anyone that uses it.

3 Likes

One thing I’d like to add.

Before polishing I like to work without key snapping and sometimes I scale curves… so most of the time my keys are on subframes. But when I polish stuff I’d like to clean it up to put keys on certain places.

And here “Bake Action” fails. For some reason operator skips subframe keyframes in baking and they are just stay in place afterward. I have to clean 'em up by hands. Then I found that “Bake Curve” → “Unbake Curve” works just fine in this case.

I agree that both having “Bake Action” and “Bake Curve” is confusing for users and needs to be reworked. But please note the issue I’ve described above.

And one more…

In such case “Bake Curve” works instantly, not like “Bake Action”

that points to the bake action needing improvement
and that we need a nice baking solution outside the NLA as well (baking in the sense of adding keys)

There is also the Bake Sound to F-Curves operator which I think generates a baked FCurve. Would you want to change it so that it generates a normal FCurve instead? Here you would hit the worst case behavior that you describe.

2 Likes

I don’t really have much of a stake in this, but to me it makes sense to tailor the data storage to potential use cases. Making a clear separation between the cases of interpolated curve keyframes and dense data seems quite reasonable-- for geometry, we have the same differentiation between poly curves and Bezier curves. I’d imagine dense animation data would come up fairly often. Using a much smaller data format will always allow better performance in the end, if the code is properly optimized.

I’d like to point out the memory usage of FPoint could be halved if the selection state was removed or moved elsewhere (see ⚙ D9199 Remove unnecessary fields from FPoint). Then FPoint could just be float2, which would make performance improvements easier since generic non-animation specific code could be used.

2 Likes

Bake action- leaving existing frames instead of replacing them is a known issue unless you bake to a new action…so it makes sense that the workaround would be use this to resample the curve but it should be fixed in Bake action (I have a long list of needs for improved baking)

I am for removal since storage is cheap and loosing data isn’t but not before we have

  • a solution to bake sound to regular F-curve data needs to be addressed before removal.
  • Fixing or making “Bake” channel aka improving Bake action to be more flexible and generic bake operation is needed so that we can not have two bakes that add confusion.
  • Any bake channel operator has to be as fast as the bake/unbake operator meaning it can do the work without having to step through the graph, as the current bake and unbake are instant with very long frame counts.

for 4.0 I would be happy with renaming it to something like Sample/Resample or Convert to samples Convert to keys so that it is clear what it is doing vs. the Bake action, bake tools.

@jacqueslucke and @HooglyBoogly
good point about the Bake Sound to F-Curves and tailoring the data storage to potential use cases. From a user perspective I have 2 issues with the Bake Curve that you might know how to resolve differently.

  1. It is destructive
  2. It is non editable

The destructiveness I think we can’t change. If we want to optimize storage data needs to be removed. However I am wondering if we can keep it editable.
Currently FCurve has two arrays bezt and fpt, each storing their respective structs. Now that almost everything is C++, can we merge that into a single Vector<abstract_key> that can hold them both? If all editing operations are done with abstract_key they would stay editable. You could convert sections of the curve to the smaller version saving disk space as needed. Let me know what you think.

@Bclark
renaming it is a good idea for 4.0
I think the timeline was a bit too aggressive on deprecating it.

For 4.0 I think we can get a Bake Channel operator in. Do you have a list features that such an operator should support?

Another possibility I’ve been pondering would be to just split the data up differently. So rather than having two arrays for two completely different kinds of curves, we instead have multiple arrays for different parts of the data for the same keys. In other words, more of a data-oriented design approach.

It could look something like this:

struct FCurve {
  Vector<KeyFlags> key_flags;
  Vector<float> key_time;        
  Vector<float> key_value;
  Vector<BezHandles> key_handles;
  ...
}

All arrays would either match the length of the total number of keys, or would be empty if unused. key_value and key_flags would always be allocated and used. Per-frame baked curves would stop there. A linear-or-constant-only fcurve would further allocate key_time. And an fcurve with bezier keys would further allocate the key_handles array.

This would also lend itself to fairly straightforward extension with new key types (e.g. quaternion keys where all four channels are always keyed together), without forcing a combinatoric explosion of types for e.g. per-frame-baked quaternions vs exclusively slerped/constant quaternions vs full smooth interpolated quaternions. And it would also allow easier sharing of code for e.g. finding what key indices we’re interpolating between during evaluation.

The down side is we’d have to be a bit more disciplined in making sure that invariants are maintained and that the fcurve can’t get into invalid states.

2 Likes

I would recommend against trying to abstract away the changed data type. After all, the different data type is exactly what would allow better performance and simpler code.

I find what @Cessen is suggesting compelling. Though I’d go a bit further and split the KeyFlags into separate BitVectors so you could tell when nothing is selected, for example. That struct-of-array storage is definitely a different way of thinking about things, but it has so many benefits IMO. For CurvesGeometry we did split Bezier handles and points into separate arrays too (though that part I’m less convinced about in retrospect, at least for geometry).

I am working on getting a mega document for improved “Bake animation” features, that I started as a brain dump but will post here when it is human readable.
In short term simple replace the current BAKE curve with a Bake channel, I guess the feature list would be what the Un-bake does now but with more control over resulting keytype and smart bake curve fit options :slight_smile:

Thanks for all the feedback. Really appreciated. It showed that this won’t be feasible to completely replace for 4.0, so I changed the roadmap.

I am proposing to rename the operator in 4.0 (still a breaking change though)
We will also add a “Bake Channel” operator
For this I made a Todo #111050: Better Baking in the Graph Editor

In the 4.x versions we will improve baking and F-Curve storage
If we succeed in that and Sampled F-Curves are no longer needed, we can discard them in 5.0

:+1: I’d like to propose changing “baking” to “freezing”. Everybody (except some dilusional cooks) knows that freezing & subsequent thawing of most foods will change the consistency. Freeze/thaw is also conceptually different from lock/unlock, as I wouldn’t expect the latter to actually change the data.

I also quite like @cessen’s proposal for the struct-of-arrays approach. And since we want to make dense animation workflows possible, maybe the freeze/thaw operator can later be replaced by explicit conversions between ‘normal’ and ‘dense’.

1 Like

I’d prefer calling them “Samples” as I did in this PR #111049: Animation: Rename “Bake Curve” to “Keys to Samples”

It is already called like that in code and it makes sense in the context of baking sound to samples. Personally I don’t like the word freezing because it is unclear to me what the outcome of that operation is.
But I also think this should be discussed in the next A&R meeting

2 Likes

Another use case to keep in mind as we design how Blender thinks about f-curves: looping animations.

I would love to see Blender support looping animation as a first-class use case, and I suspect actually doing that well (i.e. without footguns for the user) will involve Blender interpreting animation data differently for loops. For example, even with just a single key, Blender would know that the key repeats with a period N, and those repeats of that one key are then interpolated.

I’d prefer it if we moved cyclic animation features into the tooling of the new animation data model.
I think adding this to the FCurves is the wrong place because:

  • FCurves don’t really represent animation, they just map value x to value y
  • There is no start and end to them. Yes the user can ensure to always keep first and last key keyed on all channels. But that is unreliable

Whereas with the new Animation data model we have strips that are an actual container for an animation. Animation as in a collection of FCurves with an intent, a start and an end. This can be easily looped and also easily managed to insert keys at the right place of the FCurve.

I agree that looping probably shouldn’t be specified on a per-f-curve basis, and that strips are a better place for that. Per-f-curve looping is obnoxious to manage as a user, and is one of the reasons Blender’s current approach to looping animations (based on f-curve modifiers) is kind of a nightmare.

But I do think the animation system should understand how to interpret f-curves as looping, without requiring the animator (even with the aid of tooling) to maintain identical matching key frames at the start/end of the loop. Those identical matching keys are actually the same key, and IMO should only be represented once. And allowing that requires knowledge of looping at f-curve evaluation time, which is what I was getting at.

I don’t think that will impact how f-curves are stored. But I want to make sure we keep it in mind, just in case we might otherwise make a decision at this level that unexpectedly makes that approach to looping difficult.