2 unstable releases
new 0.2.2 | Nov 24, 2024 |
---|---|
0.2.1 |
|
0.2.0 |
|
0.1.0 | Oct 16, 2024 |
#105 in Graphics APIs
31 downloads per month
Used in 6 crates
61KB
1K
SLoC
wgcore − utilities and abstractions for composable WGSL shaders
wgcore provides simple abstractions over shaders and gpu resources based on wgpu
. It aims to:
- Expose thin wrappers that are as unsurprising as possible. We do not rely on complex compiler
magic like bitcode generation in frameworks like
cust
andrust-gpu
. - Provide a proc-macro (through the
wgcore-derive
crate) to simplifies shader reuse across crates with very low boilerplate. - No ownership of the gpu device and queue. While
wgcore
does expose a utility structgpu::GpuInstance
to initialize the compute unit, it is completely optional. All the features ofwgcore
remain usable if the gpu device and queue are already own by, e.g., a game engine.
Shader composition
Basic usage
Currently, wgcore relies on naga-oil for shader
composition. Though we are keeping an eye on the ongoing WESL
effort for an alternative to naga-oil
.
The main value added over naga-oil
is the wgcore::Shader
trait and proc-macro. This lets you
declare composable shaders very concisely. For example, if the WGSL sources are at the path
./shader_sources.wgsl
relative to the .rs
source file, all that’s needed for it to be composable
is to derive
she Shader
trait:
#[derive(Shader)]
#[shader(src = "shader_source.wgsl")]
struct MyShader1;
Then it becomes immediately importable (assuming the .wgsl
source itself contains a
#define_import_path
statement) from another shader with the shader(derive)
attribute:
#[derive(Shader)]
#[shader(
derive(MyShader1), // This shader depends on the `MyShader1` shader.
src = "kernel.wgsl", // Shader source code, will be embedded in the exe with `include_str!`.
)]
struct MyShader2;
Finally, if we want to use these shaders from another one which contains a kernel entry-point,
it is possible to declare ComputePipeline
fields on the struct deriving Shader
:
#[derive(Shader)]
#[shader(
derive(MyShader1, MyShader2),
src = "kernel.wgsl",
)]
struct MyKernel {
// Note that the field name has to match the kernel entry-point’s name.
main: ComputePipeline,
}
This will automatically generate the necessary boiler-place for creating the compute pipeline
from a device: MyKernel::from_device(device)
.
Some customization
The Shader
proc-macro allows some customizations of the imported shaders:
src_fn = "function_name"
: allows the input sources to be modified by an arbitrary string transformation function before being compiled as a naga module. This enables any custom preprocessor to run before naga-oil.shader_defs = "function_name"
: allows the declaration of shader definitions that can then be used in the shader in, e.g.,#ifdef MY_SHADER_DEF
statements (as well as#if
statements and anything supported by thenaga-oil
’s shader definitions feature).composable = false
: specifies that the shader does not exports any reusable symbols to other shaders. in particular, this must be specified if the shader sources doesn’t contain any#define_import_path
statement.
#[derive(Shader)]
#[shader(
derive(MyShader1, MyShader2),
src = "kernel.wgsl",
src_fn = "substitute_aliases",
shader_defs = "dim_shader_defs"
composable = false
)]
struct MyKernel {
main: ComputePipeline,
}
Dependencies
~15–47MB
~764K SLoC