Fix #119360: Precision issue with cycle modifier
The issue was a floating point precision issue between the number of the cycle and the time within the cycle (`cycle` and `cyct`). Due to that the `cycle` would advance to the next whole number while the `cycle time` is still slightly under that. This is fixed by calculating the `cycle` with double precision. This has a measurable performance impact (for `fcm_cycles_time`) | Before | After | | - | - | | 39ns | 44ns | For fcurves with a cycle modifier, this code is run at every evaluate call. The only time when that would be an issue is in the graph editor, where there is an evaluate call for roughly every pixel for curves that have a modifier. However even in that scenario the performance is the same within run to run variance (for `graph_draw_curves`) | Before | After | | - | - | | 91565ns | 91430ns | Pull Request: https://projects.blender.org/blender/blender/pulls/123222
This commit is contained in:
parent
6c29892909
commit
7e9b580546
@ -624,7 +624,7 @@ static float fcm_cycles_time(
|
||||
{
|
||||
const FMod_Cycles *data = (FMod_Cycles *)fcm->data;
|
||||
tFCMED_Cycles *storage = static_cast<tFCMED_Cycles *>(storage_);
|
||||
float prevkey[2], lastkey[2], cycyofs = 0.0f;
|
||||
float firstkey[2], lastkey[2], cycyofs = 0.0f;
|
||||
short side = 0, mode = 0;
|
||||
int cycles = 0;
|
||||
float ofs = 0;
|
||||
@ -642,11 +642,11 @@ static float fcm_cycles_time(
|
||||
|
||||
/* calculate new evaltime due to cyclic interpolation */
|
||||
if (fcu->bezt) {
|
||||
const BezTriple *prevbezt = fcu->bezt;
|
||||
const BezTriple *lastbezt = prevbezt + fcu->totvert - 1;
|
||||
const BezTriple *firstbezt = &fcu->bezt[0];
|
||||
const BezTriple *lastbezt = &fcu->bezt[fcu->totvert - 1];
|
||||
|
||||
prevkey[0] = prevbezt->vec[1][0];
|
||||
prevkey[1] = prevbezt->vec[1][1];
|
||||
firstkey[0] = firstbezt->vec[1][0];
|
||||
firstkey[1] = firstbezt->vec[1][1];
|
||||
|
||||
lastkey[0] = lastbezt->vec[1][0];
|
||||
lastkey[1] = lastbezt->vec[1][1];
|
||||
@ -656,8 +656,8 @@ static float fcm_cycles_time(
|
||||
const FPoint *prevfpt = fcu->fpt;
|
||||
const FPoint *lastfpt = prevfpt + fcu->totvert - 1;
|
||||
|
||||
prevkey[0] = prevfpt->vec[0];
|
||||
prevkey[1] = prevfpt->vec[1];
|
||||
firstkey[0] = prevfpt->vec[0];
|
||||
firstkey[1] = prevfpt->vec[1];
|
||||
|
||||
lastkey[0] = lastfpt->vec[0];
|
||||
lastkey[1] = lastfpt->vec[1];
|
||||
@ -667,12 +667,12 @@ static float fcm_cycles_time(
|
||||
* 1) if in data range, definitely don't do anything
|
||||
* 2) if before first frame or after last frame, make sure some cycling is in use
|
||||
*/
|
||||
if (evaltime < prevkey[0]) {
|
||||
if (evaltime < firstkey[0]) {
|
||||
if (data->before_mode) {
|
||||
side = -1;
|
||||
mode = data->before_mode;
|
||||
cycles = data->before_cycles;
|
||||
ofs = prevkey[0];
|
||||
ofs = firstkey[0];
|
||||
}
|
||||
}
|
||||
else if (evaltime > lastkey[0]) {
|
||||
@ -690,17 +690,18 @@ static float fcm_cycles_time(
|
||||
/* find relative place within a cycle */
|
||||
{
|
||||
/* calculate period and amplitude (total height) of a cycle */
|
||||
const float cycdx = lastkey[0] - prevkey[0];
|
||||
const float cycdy = lastkey[1] - prevkey[1];
|
||||
const float cycdx = lastkey[0] - firstkey[0];
|
||||
const float cycdy = lastkey[1] - firstkey[1];
|
||||
|
||||
/* check if cycle is infinitely small, to be point of being impossible to use */
|
||||
if (cycdx == 0) {
|
||||
return evaltime;
|
||||
}
|
||||
|
||||
/* calculate the 'number' of the cycle */
|
||||
const float cycle = (float(side) * (evaltime - ofs) / cycdx);
|
||||
|
||||
/* Calculate the 'number' of the cycle. Needs to be a double to combat precision issues like
|
||||
* #119360. With floats it can happen that the `cycle` jumps to the next full number, while
|
||||
* `cyct` below is still behind. */
|
||||
const double cycle = side * (double(evaltime) - double(ofs)) / double(cycdx);
|
||||
/* calculate the time inside the cycle */
|
||||
const float cyct = fmod(evaltime - ofs, cycdx);
|
||||
|
||||
@ -730,10 +731,10 @@ static float fcm_cycles_time(
|
||||
|
||||
/* special case for cycle start/end */
|
||||
if (cyct == 0.0f) {
|
||||
evaltime = (side == 1 ? lastkey[0] : prevkey[0]);
|
||||
evaltime = (side == 1 ? lastkey[0] : firstkey[0]);
|
||||
|
||||
if ((mode == FCM_EXTRAPOLATE_MIRROR) && (int(cycle) % 2)) {
|
||||
evaltime = (side == 1 ? prevkey[0] : lastkey[0]);
|
||||
evaltime = (side == 1 ? firstkey[0] : lastkey[0]);
|
||||
}
|
||||
}
|
||||
/* calculate where in the cycle we are (overwrite evaltime to reflect this) */
|
||||
@ -744,7 +745,7 @@ static float fcm_cycles_time(
|
||||
* (result of fmod will be negative, and with different phase).
|
||||
*/
|
||||
if (side < 0) {
|
||||
evaltime = prevkey[0] - cyct;
|
||||
evaltime = firstkey[0] - cyct;
|
||||
}
|
||||
else {
|
||||
evaltime = lastkey[0] - cyct;
|
||||
@ -752,9 +753,9 @@ static float fcm_cycles_time(
|
||||
}
|
||||
else {
|
||||
/* the cycle is played normally... */
|
||||
evaltime = prevkey[0] + cyct;
|
||||
evaltime = firstkey[0] + cyct;
|
||||
}
|
||||
if (evaltime < prevkey[0]) {
|
||||
if (evaltime < firstkey[0]) {
|
||||
evaltime += cycdx;
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user