Allow customization of shader compilation
- Dominant language
- Rust
- Stars
- 48.2k
- Forks
- 4.8k
- Avg merge
- 4d 8m
- Merged PRs (30d)
- 147
Description
## What problem does this solve or what need does it fill?
Naga is too immature for production use, but it's not currently possible to circumvent it with Bevy's shader compilation pipeline.
Random assortment of issues and limitations with the current compilation model:
- Always compiles to the lowest common denominator (WGSL), but WGSL is lacking tons of features that are present in GLSL, HLSL and MSL (not to mention SPIR-V).
- One example that I ran into this morning is that using GLSL `isinf()` or `isnan()` just returns a compilation error.
- Another example is the lack of `float16` in WGSL, which is standard on all newer hardware.
- Naga is still pretty buggy.
- The `Shader` asset is really a representation of the shader's source code, where the actual compilation is implicit in the pipeline (re)creation step. This means you couldn't, for example, write a custom `AssetLoader` that circumvents Naga and remain compatible with the entire render resource system.
- The WGSL common denominator is "good enough" for really simple vertex/fragment shaders, but compute shaders have a tendency to exercise more advanced features, so it's very easy to run into these limitations.
- `wgsl-analyzer` is frequently behind Naga, so the development experience in GLSL is actually superior much of the time.
I want to say that I think all of these limitations are totally understandable, given that the WGSL spec is still in flux and Naga is a young project, but in the mean time it would be nice to be able to get somewhere while they catch up.
For my custom compute pipelines, I've had to circumvent the render resource system, which means I'm just using raw `wgpu::CompilePipeline`s, without going through `PipelineCache`. I'm using the much more mature `shaderc` to compile my shaders to SPIR-V and then passing them to `wgpu::Device::create_shader_module_spirv` (unsafe). This means I don't get hot reloading of shaders, but I'm able to use many more features. I also forgo all shader-related parts of the pipeline validation.
## What solution would you like?
I would love to be able to somehow instruct Bevy to use `shaderc` to compile the shaders and punch a hole through to `wgpu::Device::create_shader_module_spirv` as a workaround, at least until WGSL and Naga catch up.
This could either be a configuration parameter to the asset system, or it could be the ability to write a custom `AssetLoader` that does it. Maybe `Shader` should be able to contain raw compiled SPIR-V, in addition to the shader source code.
## What alternative(s) have you considered?
Some of these issues can be worked around in shader code. For example, I implemented my own `isinf()` and `isnan()` using `floatBitsToUint()` (GLSL), but this is much less feasible with things like `float16` support.
## Additional context
You may wonder why I need explicit `float16` support in shader code, rather than simply using `float16` texture formats. The reason is that I'm implementing an algorithm in a compute shader where threads are cooperating (in a limited sense), so I need to compare the results of neighboring threads. This works fine using 32-bit floats, but I don't need the precision and the memory requirements are a factor. So I want to use `float16` as storage, but when comparing the stored values I need to account for the precision loss. The most stable way to do that is to convert values to `float16` in shader code before comparing them (with an ULPS epsilon suitable for the 16-bit representation).
Contributor guide
Assessment
This issue has not been assessed yet.