@if for conditions - preview
WESL M1 includes a feature for conditional translation, described here: https://wesl-lang.dev/spec/ConditionalTranslation.
Many WGSL users code up their own approaches to conditions. Easing that effort with a common approach simplifies things and enables common tooling. Probably conditions should eventually land in WGSL too..
Do you see anything in WESL's current @if plans that could be improved? in design, or fit with WGSL? We'll learn more as users try conditions in WESL, but perhaps there's something you see now that we should adjust? If so, we can tweak things to make the upcoming user trials with conditions gather more useful info.
If it's not too much trouble, there's a part of me that wonders if this should integrate a bit more with spec constants / pipeline overrides, rather than being its own separate thing.
In a lot of cases, I imagine normal if statements to be like this already. e.g.
@override var use_raytracing: bool = false;
fn main() {
if (use_raytracing) {
cast_rays();
} else {
raymarch();
}
}
should already compile equivalently to your @if suggestion. The annoying thing in this case is that both arms of the if statement are considered statically accessed for the purposes of interface binding; so any bindings used by cast_rays are included in the shader interface. That could be worked around if we had the ability to "turn on/off" variables based on pipeline overrides as well;
@override var use_raytracing: bool = false;
@if(use_raytracing) @group(0) @binding(0) var<storage> tlas: TLAS;
@if(!use_raytracing) @group(0) @binding(1) var<storage> sdf: SDF;
fn cast_rays() { load(tlas); }
fn raymarch() { load(sdf); }
That's a somewhat bigger design space, but it might be worth chasing?
Definitely not too much trouble. The direction you're thinking sounds worthy and the conditions design will certainly evolve.
it's a good point about whether to remove references from statically dead code in @if or if.
Another bit consider: modifying at build time vs. submit time. e.g. override comes with a promise that flexibility remains at submit time, but some would like to statically remove code at build time.
I like that overrides are declared. Conditions are currently not declared, they're just used in WESL M1. But declaration seems handy for a future rev when we want to manage modularity in libraries. (I think library consumers ought to be in control)
It might be worth having some type of dead-code elimination in WGSL that can be used, like if constexpr in C++ (not advocating for this specific syntax). This way the static use rules to generate the shader reflection would take care of removing the binding.
If we add the "structs of bindings" approach then static usage possibly don't apply (see #4957); in that case, an @if() directive for enabling/disabling variables and struct members seems like an OK idea.
There are enough side effects from unused references in disabled conditions that I think we'll need something beyond just WGSL's current if(), even aside from conditional declarations and conditional struct members.
e.g. if we have this code:
statically_if (ray_tracing) { unused(); }
unused() might reference some things that cause side effects that the programmer would like to avoid. Basically unused() might be the tip of a large iceberg of code that the programmer doesn't want in the assembled shader and might fail to validate. e.g. the ray tracing module might have const_assert that would unexpectedly fail in a non ray_tracing build.
I like how if constexpr is more in-the-language syntactically than attribute syntax like @if. (And users are demanding @else already, so there'd be more attribute syntax to come) constexpr is a bit wordy, though, wdyt? There's also static if (from dlang). @k2d222.
@magcius also brought up override vars vs @if condition vars. Worth exploring too. There's some overlap in functionality. It'd be nice if there was one way to pass runtime variables to tweak shaders via conditions, not two.
Also @if condition vars are just implicit in WESL right now, they're not declared like override vars. But I bet conditions will want declarations, so that conditions can be managed for modularity. For example, an app shader that uses a module with conditions should be able to fix condition values for the module, and not expose the conditions to host code. If condition variables are going to need declarations with optional initial values, they're going to look even closer to override declarations.
Editorial feedback
Grammar for translate-time expression includes too much?
Right now the way things are worded, translate-time expressions include logical expressions and parenthesized expressions. But formally those pull in generalized subexpressions. I think you mean for the terms to be only translate-time features, boolean literal values, and then the only way to combine them are via !, &&, || and parens. That is, rather than referencing the current WGSL grammar elements of logical expresison and parenthesized expression, make your own hirearchy for this.
Substantive feedback:
What combinations are validated?
When conditional compilation has compound conditions, then it's not clear what level of validation is required by a parser/validator: What combinations of conditions are to be validated? As various discussions point out there's a combinatorial explosion of these. Forcing the conditions to be declared might help; then you can designate only a few of them to be validated. That is, some declared translate-time-expressions are used only for refactoring and readability, and others are required to be validated by a front-end.
What if disabled code includes syntax the translator doesn't understand?
It's not clear to me if this applies. What if some conditional targets introduce new syntax. Is a compilation for other targets supposed to understand that syntax so it can skip over it? Or is that already handled by the fact you assume WESL has a single syntax already, and skipped code is handled at a grammar-term level?