-
-
Notifications
You must be signed in to change notification settings - Fork 4k
Preconvert colors before sending to shader #20074
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Preconvert colors before sending to shader #20074
Conversation
Previously attempted in #20073 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implementation is fine. The snag is that interpolation in eight more colour spaces has been added since the initial gradients PR (not sure why there aren't merge conflicts). So this is a bit more involved and needs a match statement or something to select a preconversion method. Also the hues should be normalized for interpolation in cylindrical color spaces.
} | ||
|
||
// Only color interpolation in SRGB space is supported atm. | ||
return mix_linear_rgb_in_srgb_space(start_color, end_color, t); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is now:
#ifdef IN_SRGB
return mix_linear_rgba_in_srgba_space(start_color, end_color, t);
#else ifdef IN_OKLAB
return mix_linear_rgba_in_oklaba_space(start_color, end_color, t);
#else ifdef IN_OKLCH
return mix_linear_rgba_in_oklcha_space(start_color, end_color, t);
#else ifdef IN_OKLCH_LONG
return mix_linear_rgba_in_oklcha_space_long(start_color, end_color, t);
#else ifdef IN_HSV
return mix_linear_rgba_in_hsva_space(start_color, end_color, t);
#else ifdef IN_HSV_LONG
return mix_linear_rgba_in_hsva_space_long(start_color, end_color, t);
#else ifdef IN_HSL
return mix_linear_rgba_in_hsla_space(start_color, end_color, t);
#else ifdef IN_HSL_LONG
return mix_linear_rgba_in_hsla_space_long(start_color, end_color, t);
#else
return mix(start_color, end_color, t);
#endif
It'd be nice to have some performance profiling data too, even though this seems like it will be an obvious improvement. |
Is there examples of how to do this? That is the performance profiling. |
I normally use tracy, there's a section on using it with bevy here: But for this it would be fine just to copy from the benchmark examples in |
1fc67d9
to
fff060d
Compare
Performance ResultsTested with 900 gradients using a new stress test example on MacBook Pro M4: Color Space FPS Comparison
With Animation Enabled (OkLab)
Not much difference, but idk if a bigger improvement is expected. |
Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke! If it's expected, please add the M-Deliberate-Rendering-Change label. If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it. |
1 similar comment
Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke! If it's expected, please add the M-Deliberate-Rendering-Change label. If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it. |
Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke! If it's expected, please add the M-Deliberate-Rendering-Change label. If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it. |
Yeah, I would expect this to be slight. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks very good, the complexity is reduced and the performance gains are actually quite significant.
Just needs some changes to fix the fill modes.
mix(a.w, b.w, t) | ||
); | ||
} | ||
|
||
fn interpolate_gradient( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interpolation gets skipped when the FILL_START/END
flags are set so start_color
and end_color
are still in the interpolation color space and need to be converted to linear rgba before they are returned.
@@ -426,23 +383,23 @@ fn interpolate_gradient( | |||
} else { | |||
t = 0.5 * (1 + (t - hint) / (1.0 - hint)); | |||
} | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed version of interpolate_gradient
I wrote to test it after I thought I'd figured out what was wrong with the fill modes:
fn interpolate_gradient(
distance: f32,
start_color: vec4<f32>,
start_distance: f32,
end_color: vec4<f32>,
end_distance: f32,
hint: f32,
flags: u32,
) -> vec4<f32> {
if start_distance == end_distance {
if distance <= start_distance && enabled(flags, FILL_START) {
return convert_to_linear_rgba(start_color);
}
if start_distance <= distance && enabled(flags, FILL_END) {
return convert_to_linear_rgba(end_color);
}
return vec4(0.);
}
var t = (distance - start_distance) / (end_distance - start_distance);
if t < 0.0 {
if enabled(flags, FILL_START) {
return convert_to_linear_rgba(start_color);
}
return vec4(0.0);
}
if 1. < t {
if enabled(flags, FILL_END) {
return convert_to_linear_rgba(end_color);
}
return vec4(0.0);
}
if t < hint {
t = 0.5 * t / hint;
} else {
t = 0.5 * (1 + (t - hint) / (1.0 - hint));
}
return convert_to_linear_rgba(mix_colors(start_color, end_color, t));
}
// Mix the colors, choosing the appropriate interpolation method for the given color space
fn mix_colors(
start_color: vec4<f32>,
end_color: vec4<f32>,
t: f32,
) -> vec4<f32> {
#ifdef IN_OKLCH
return mix_oklcha(start_color, end_color, t);
#else ifdef IN_OKLCH_LONG
return mix_oklcha_long(start_color, end_color, t);
#else ifdef IN_HSV
return mix_hsva(start_color, end_color, t);
#else ifdef IN_HSV_LONG
return mix_hsva_long(start_color, end_color, t);
#else ifdef IN_HSL
return mix_hsla(start_color, end_color, t);
#else ifdef IN_HSL_LONG
return mix_hsla_long(start_color, end_color, t);
#else
// Just lerp in linear RGBA, OkLab and SRGBA spaces
return mix(start_color, end_color, t);
#endif
}
// Convert a color from the interpolation color space to linear rgba
fn convert_to_linear_rgba(
color: vec4<f32>,
) -> vec4<f32> {
#ifdef IN_OKLCH
return oklcha_to_linear_rgba(color);
#else ifdef IN_OKLCH_LONG
return oklcha_to_linear_rgba(color);
#else ifdef IN_HSV
return hsva_to_linear_rgba(color);
#else ifdef IN_HSV_LONG
return hsva_to_linear_rgba(color);
#else ifdef IN_HSL
return hsla_to_linear_rgba(color);
#else ifdef IN_HSL_LONG
return hsla_to_linear_rgba(color);
#else ifdef IN_OKLAB
return oklaba_to_linear_rgba(color);
#else ifdef IN_SRGB
return vec4(pow(color.rgb, vec3(2.2)), color.a);
#else
// Color is already in linear rgba space
return color;
#endif
}
ColorStop::new(RED, Val::Percent(0.0)), | ||
ColorStop::new(BLUE, Val::Percent(100.0)), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Needs more colour stops to capture the full benefits, otherwise the gains are masked by the overhead from layout. With this gradient:
ColorStop::auto(RED),
ColorStop::auto(BLUE),
ColorStop::auto(GREEN),
ColorStop::auto(YELLOW),
ColorStop::auto(ORANGE),
ColorStop::auto(LIME),
ColorStop::auto(DARK_CYAN),
I get ~30% better FPS compared to main.
44f72a9
to
60dd402
Compare
Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke! If it's expected, please add the M-Deliberate-Rendering-Change label. If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it. |
Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke! If it's expected, please add the M-Deliberate-Rendering-Change label. If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The HSL and HSV conversions are off a bit. If you look at the gradients
example the bottom set of striped gradients uses hard stops so there is no interpolation (the conic excepted) so the gradients should look the same for each colour space, but in HSL and HSV you can see that the colours are lighter:
In Oklaba

In Hsla

There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually I think the problem with the HSL conversions is in main too, except that when there is a hard stop the colour is already linear RGBA so it isn't converted. Everything else is okay, once the CI is happy I think this is good to merge.
Co-authored-by: ickshonpe <david.curthoys@googlemail.com>
Your PR caused a change in the graphical output of an example or rendering test. This might be intentional, but it could also mean that something broke! If it's expected, please add the M-Deliberate-Rendering-Change label. If this change seems unrelated to your PR, you can consider updating your PR to target the latest main branch, either by rebasing or merging main into it. |
Ping me when CI is green and I'll do a review and merge this :) The Dependencies failure is not your fault. |
@alice-i-cecile CI is green |
Objective
Solution
prepare_gradient
ingradient.rs
to convert colors fromLinearRgba
toSrgba
on the CPU before sending to the GPUThis optimization reduces the number of power operations per pixel from 3 to 1:
Testing
cargo run --example gradients
) compile and render correctlyTo test:
cargo run --example gradients
orcargo run --example stacked_gradients