bevyengine/bevy · error · AutoExposureCompensationCurveError

discontinuity found between curve segments

Error message

discontinuity found between curve segments

What it means

After building the cubic curve, `from_curve` walks every segment and requires each one to start exactly where the previous sample ended (`segment.position(0.0) == previous`). A gap or jump in the log-luminance (x) domain between segments returns `AutoExposureCompensationCurveError::DiscontinuityFound` (crates/bevy_post_process/src/auto_exposure/compensation_curve.rs:120), because the LUT bake assumes a continuous curve.

Source

Thrown at crates/bevy_post_process/src/auto_exposure/compensation_curve.rs:49

    /// The lookup table for the curve. Uploaded to the GPU as a 1D texture.
    /// Each value in the LUT is a `u8` representing a normalized exposure compensation value:
    /// * `0` maps to `min_compensation`
    /// * `255` maps to `max_compensation`
    ///
    /// The position in the LUT corresponds to the normalized log luminance value.
    /// * `0` maps to `min_log_lum`
    /// * `LUT_SIZE - 1` maps to `max_log_lum`
    lut: [u8; LUT_SIZE],
}

/// Various errors that can occur when constructing an [`AutoExposureCompensationCurve`].
#[derive(Error, Debug)]
pub enum AutoExposureCompensationCurveError {
    /// The curve couldn't be built in the first place.
    #[error("curve could not be constructed from the given data")]
    InvalidCurve,
    /// A discontinuity was found in the curve.
    #[error("discontinuity found between curve segments")]
    DiscontinuityFound,
    /// The curve is not monotonically increasing on the x-axis.
    #[error("curve is not monotonically increasing on the x-axis")]
    NotMonotonic,
}

impl Default for AutoExposureCompensationCurve {
    fn default() -> Self {
        Self {
            min_log_lum: 0.0,
            max_log_lum: 0.0,
            min_compensation: 0.0,
            max_compensation: 0.0,
            lut: [0; LUT_SIZE],
        }
    }
}

View on GitHub (pinned to 396ca72708)

Solutions

  1. Check that x values are contiguous across the whole input — no skipped log-luminance values between segments
  2. Rebuild the curve from a single sorted, gap-free point list
  3. Validate the junction points programmatically before calling `from_curve`

Example fix

// before: gap between segments -> DiscontinuityFound
let pts = [vec2(-4.0, -2.0), vec2(-1.0, -1.0), vec2(2.0, 1.0)]; // jump 2.0 -> skipped range

// after: dense, contiguous sampling of the intended function
let pts = (-40..=40).map(|i| vec2(i as f32 / 10.0, (i as f32 / 40.0).powi(3))).collect::<Vec<_>>();
let curve = AutoExposureCompensationCurve::from_points(pts)?;
Defensive patterns

Strategy: try-catch

Validate before calling

let mut pts: Vec<Vec2> = points.to_vec();
pts.sort_by(|a, b| a.x.partial_cmp(&b.x).unwrap());
// contiguity sanity check before handing to the converter
for w in pts.windows(2) {
    assert!(w[1].x > w[0].x, "duplicate/decreasing x values");
}

Try / catch

match AutoExposureCompensationCurve::from_points(pts) {
    Err(AutoExposureCompensationCurveError::DiscontinuityFound) => {
        warn!("gaps in compensation curve; falling back to linear interpolation of endpoints");
        fallback_curve(pts)
    }
    other => other?,
}

Prevention

When it happens

Trigger: Feeding `from_points`/`from_curve` data whose consecutive segments don't chain on the x-axis: points with a gap between segment boundaries, or a custom `CubicGenerator` whose segments cover disjoint intervals.

Common situations: Hand-authored compensation curves from spreadsheets/JSON where an x value was skipped or mistyped; splicing two curves together without blending the junction.

Related errors


AI-assisted analysis of bevyengine/bevy@396ca72708 (2026-08-20). Data as JSON: /api/errors/da9fae9b77d858d3. Report an issue: GitHub.