Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

Toolchain: `nightly-2026-08-15` (rustc 1.99.0)

We're written a new "Getting started" section for rust-gpu with the new `cargo gpu generate` command to set up a rust-gpu workspace for you. This may be interesting for existing users too for an introduction how we imagine a state-of-the-art rust-gpu setup looks like.

### 🚨BREAKING🚨️

- [PR#661](https://github.com/Rust-GPU/rust-gpu/pull/661) adds support for glam v0.34 and makes it the default glam version
Expand Down
16 changes: 2 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,13 @@ See [the examples](examples/) for full details.

## Getting started

Check out [The `rust-gpu` Dev Guide][gpu-guide] for information on how to get started with using it in your projects.

Experiment with rust-gpu shaders in-browser at [SHADERed][shadered].
Check out [our docs][gpu-guide] on how to get started using it in your projects.

[gpu-guide]: https://rust-gpu.github.io/rust-gpu/book/
[shadered]: https://shadered.org/shaders?language=rust&sort=hot

## Getting help

For questions and comments, we use [GitHub discussions](https://github.com/Rust-GPU/rust-gpu/discussions) to keep conversations organized and searchable. This also makes it easy to escalate relevant topics to GitHub issues if needed. We do not have an official chat room.
For questions and comments, we use [GitHub discussions](https://github.com/Rust-GPU/rust-gpu/discussions) to keep conversations organized and searchable. This also makes it easy to escalate relevant topics to GitHub issues if needed. There's also a semi-official [discord](https://discord.gg/mDUDgsbBq6).

## Background

Expand Down Expand Up @@ -107,15 +104,6 @@ We use this repo as a monorepo for everything related to the project: crates, to

Right now because the project is in an early state of development, we might introduce temporary changes as stop-gap measures, or implement features or APIs that might not work exactly in a way we end up liking. Therefore it is expected that some (if not most) of the user facing code will change and evolve over time. At the moment this means that we make no guarantees about backwards compatibility and have no formal deprecation model in place. Effectively meaning that currently we only support building from source with the latest `main` branch in our repository. We appreciate our early adopters and would ask them to evolve their code along with ours.

## Structure

There are a few different components to this repo:

- [rfcs](docs/src/rfcs) for in-depth discussion and specs.
- [rustc_codegen_spirv](crates/rustc_codegen_spirv) for the compiler itself.
- [spirv-std](crates/spirv-std) for GPU intrinsics, types, and other library items used by GPU crates.
- [spirv-builder](crates/spirv-builder) for a convenient way of building a GPU crate in a CPU build.rs file.

## Related Projects

Historical and other related projects for compiling Rust code to GPUs.
Expand Down
14 changes: 7 additions & 7 deletions docs/src/SUMMARY.md
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# Summary

- [Introduction](./introduction.md)
- [Generating a project](./getting-started/template.md)
- [Rust-GPU workspace structure](./getting-started/structure.md)
- [Writing shaders](./getting-started/shaders.md)
- [Images](./getting-started/images.md)
- [Compute shaders](./getting-started/compute.md)
- [Contributing to Rust-GPU]()
- [Building](./building-rust-gpu.md)
- [Testing](./testing.md)
- [Debugging](./tracing)
- [Debugging]()
- [Tracing](./tracing.md)
- [Minimizing bugs in SPIR-V](./spirv-minimization.md)
- ["Codegen args" (flags/options) supported by the Rust-GPU codegen backend](./codegen-args.md)
- [Publishing Rust-GPU on crates.io](./publishing-rust-gpu.md)
- [Platform Support](./platform-support.md)
- [Writing Shader Crates](./writing-shader-crates.md)
- [Inline Assembly](./inline-asm.md)
- [Features]()
- [Attribute syntax](./attributes.md)
- [Inline Assembly](./inline-asm.md)
- [Image type syntax](./image.md)
48 changes: 0 additions & 48 deletions docs/src/building-rust-gpu.md

This file was deleted.

67 changes: 67 additions & 0 deletions docs/src/getting-started/compute.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Compute shaders

Unlike vertex or fragment shaders, compute shaders need to declare the size of their workgroup:

```rust
#[spirv(compute(threads(8, 8)))]
pub fn main() {}
```

This one declares a workgroup of the size `(8, 8, 1)`. Any dimensions not declared default to 1.

Here's a full example of a compute shader that computes `c = a + b`:

```rust
#[spirv(compute(threads(32)))]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] a: &[u32],
#[spirv(descriptor_set = 0, binding = 1, storage_buffer)] b: &[u32],
#[spirv(descriptor_set = 0, binding = 2, storage_buffer)] c: &mut [u32],
#[spirv(global_invocation_id)] global_index: UVec3,
) {
let i = global_index.x as usize;
c[i] = a[i] + b[i];
}
```

> [!CAUTION]
> I want to direct your attention to the `c: &mut [u32]` arg. In Rust, mutable references don't just allow you to mutate the referenced memory, but also gives you *exclusive* access to it. Since this mutable references is *shared* between all the threads, it can't be exclusive.
>
> This is a mistake on rust-gpu's entry point API design, not sure when I'll get around to fix it.

Here's the most interesting built-ins and intrinsics for compute shaders, put together into a rather silly shader that sum up 128 values. The shared memory is also a mutable reference that is not actually exclusive, like the mutable buffers.

```rust
// assumption: subgroup size == 32
#[spirv(compute(threads(128)))]
pub fn sum(
#[spirv(global_invocation_id)] global_invocation_id: UVec3,
#[spirv(local_invocation_id)] local_invocation_id: UVec3,
#[spirv(num_workgroups)] num_workgroups: UVec3,
#[spirv(workgroup_id)] workgroup_id: UVec3,
#[spirv(workgroup)] shared_memory: &mut [f32; 4],
#[spirv(spec_constant(id = 0, default = 42))] spec_constant: u32,
#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] values: &[f32],
#[spirv(descriptor_set = 0, binding = 1, storage_buffer)] output: &mut f32,
) {
let value = values[global_invocation_id.x as usize];
let sum_per_subgroup = spirv_std::arch::subgroup_f_add(value);

let local_idx = local_invocation_id.x as usize;
if local_idx % 32 == 0 {
shared_memory[local_idx / 32] = sum_per_subgroup;
}

spirv_std::arch::workgroup_memory_barrier_with_group_sync();
if local_idx == 0 {
let mut accum = 0.;
for i in 0..4 {
shared_memory[i] += accum;
}
*output = accum;
}
}
```

> [!Note]
> You currently can't declare the workgroup size as a specialization constant, open an issue if you need this.
45 changes: 45 additions & 0 deletions docs/src/getting-started/images.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Images

Common image formats can be found in the [`spirv_std::image`](https://rust-gpu.github.io/rust-gpu/api/spirv_std/image/index.html) module. For example, `Image2d` is a very commonly used type, corresponding to `texture2D` in GLSL, and is likely what you want if you want a regular old sampled texture. Here's an example for sampling a texture, though you'd also find many other functions in the [docs on `Image`](https://docs.rs/spirv-std/latest/spirv_std/image/struct.Image.html):

```rust,no_run
#[spirv(fragment)]
pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image2d: &Image2dI,
#[spirv(descriptor_set = 1, binding = 1)] image2d_array: &Image2dArray,
#[spirv(descriptor_set = 2, binding = 2)] cubemap: &Cubemap,
#[spirv(descriptor_set = 3, binding = 3)] sampler: &Sampler,
) {
let v2 = Vec2::new(0.0, 1.0);
let v3 = Vec3::new(0.0, 1.0, 2.0);
let r1: IVec4 = image2d.sample(*sampler, v2);
let r2: Vec4 = image2d_array.sample(*sampler, v3);
let r3: Vec4 = cubemap.sample(*sampler, v3);
}
```

Use storage images to write to images:

```rust
#[spirv(fragment)]
pub fn main(#[spirv(descriptor_set = 0, binding = 0)] storage_image: &StorageImage2d) {
let pixel = UVec2::new(123, 456);
let color = Vec4::new(0.3, 0.4, 0.5, 0.6);
unsafe {
storage_image.write(pixel, color);
}
}
```

There are a huge number of combinations of image types in SPIR-V, which are represented by a const
generic type called `spirv_std::image::Image`. Specifying the generic parameters of this
type is incredibly tedious, so we have a wrapper macro `spirv_std::Image!` to write the type
instead. The types you've used above are simply type aliases for the output of that macro:

```rust,no_run
type Image2d = Image!(2D, type=f32, sampled);
type Image2dArray = crate::Image!(2D, type=f32, sampled, arrayed);
type Cubemap = crate::Image!(cube, type=f32, sampled);
```

See the [docs of the `Image!` macro](https://rust-gpu.github.io/rust-gpu/api/spirv_std/image/macro.Image.html) for it's syntax.
117 changes: 117 additions & 0 deletions docs/src/getting-started/shaders.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
# Writing shaders

In this chapter we will have a look how to make your own shaders in rust-gpu.

This introduction is written based on [our rust-gpu template project](./template.md), we recommend setting it up locally if you want to follow along.



### Your first fragment shader

If you open up [`/mygraphics-shaders/src/lib.rs`](https://github.com/Rust-GPU/rust-gpu-template/blob/main/generated/graphics/wgpu/cargo-gpu/mygraphics-shaders/src/lib.rs), you'll find a function called `main_fs` like this:

```rust
#[spirv(fragment)]
pub fn main_fs(vtx_color: Vec3, output: &mut Vec4) {
*output = Vec4::from((vtx_color, 1.));
}
```

This is an ordinary rust function with a special macro attached to it, marking it as a fragment shader (sometimes also called pixel shader). In this demo, it is evaluated once for each pixel of our triangle and computes the color that this particular pixel should be. So if we wanted our triangle to be all red, we could change the contents of the function to:

```rust
*output = Vec4::new(1., 0., 0., 1.);
```

Now `cargo run` the project and you should see a red triangle.

Just setting the triangle to a single color is boring, so let's have a look how to declare inputs and outputs.



### Inputs and Outputs

The fragment shader above is a great example for the two basic parameters:
* `vtx_color: Vec3` parameters passed **by-value** declare an **input** to the shader. In GLSL: `in vec3 vtx_color;`.
* `output: &mut Vec4`: parameters passed **by-mutable-reference** declare an **output** to the shader. In GLSL: `out vec4 output;`.

Unlike most other shader languages, inputs and outputs can't just be declared anywhere within our code and includes. Instead, they are passed as parameters to your entry point function, centralizing where they can be declared.

> [!Note]
> While this design makes it a lot easier to figure out all the things your shader is declaring, we are aware that it makes it difficult to share these declarations across multiple shaders. We hope to rectify these problems in future rust-gpu versions.



### Built-ins and Bindings

Inputs and outputs are by far not the only declaration, so let's have a look at the vertex shader:

```rust
#[spirv(vertex)]
pub fn main_vs(
#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] constants: &ShaderConstants,
#[spirv(vertex_index)] vert_id: i32,
#[spirv(position)] vtx_pos: &mut Vec4,
vtx_color: &mut Vec3,
) {
let time = constants.time * constants.speed + vert_id as f32 * (2. * PI * 120. / 360.);
let position = vec2(f32::sin(time), f32::cos(time)) + constants.offset;
*vtx_pos = Vec4::from((position, 0.0, 1.0));

*vtx_color = [vec3(1., 0., 0.), vec3(0., 1., 0.), vec3(0., 0., 1.)][vert_id as usize % 3];
}
```

The vertex shader makes the triangle rotate by computing the vertex positions on the GPU.

It declares two built-ins that are only available vertex shader:
* `#[spirv(descriptor_set = 0, binding = 0, storage_buffer)] constants: &ShaderConstants` declares a storage buffer on the "slot" at descriptor set 0 and binding 0 in glsl terminology. This translates to bind group 0 and binding 0 in the wgpu API, we'll have more detailed look at buffers in the next section. GLSL: `layout(set = 0, binding = 0) ShaderConstants constants;`
* `#[spirv(vertex_index)] vert_id: i32` is an input built-in that is the index of the vertex. GLSL: `gl_VertexID`
* `#[spirv(position)] vtx_pos: &mut Vec4` is an output built-in to set the position of the vertex for the rasterizer. GLSL: `gl_Position`
* `vtx_color: &mut Vec3` is a standard output that is forwarded to our fragment shader. GLSL: `out vec3 vtx_color`

> [!Note]
> Unfortunately, the built-ins have magic names. You can look up most of them in [this compiletest](https://github.com/Rust-GPU/rust-gpu/blob/main/tests/compiletests/ui/spirv-attr/all-builtins.rs) and [in the compiler source code here](https://github.com/Rust-GPU/rust-gpu/blob/main/crates/rustc_codegen_spirv/src/symbols.rs#L38).



### Sharing data structures between CPU and shaders

The vertex shader declares a storage buffer of type `ShaderConstants`, which is defined in the shader crate like this:

```rust
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
#[repr(C)]
pub struct ShaderConstants {
pub time: f32,
pub speed: f32,
pub offset: Vec2,
}
```

It is just an ordinary rust struct that derives [`bytemuck::Pod`](https://docs.rs/bytemuck/latest/bytemuck/trait.Pod.html). Deriving `Pod` allows us to use the bytemuck library to convert our struct into a slice of u8s, write those bytes into a buffer on the GPU, and convert them back to our `ShaderConstants` in our shader. But to implement `Pod` our struct needs to fulfill a complex set of unsafe invariants, which the `Pod` derive macro will check for:
* It must be marked with `#[repr(C)]` so our struct has a consistent layout between our CPU compile and shader compile.
* The type must not have any padding.
* It also needs to derive [`Zeroable`](https://docs.rs/bytemuck/latest/bytemuck/trait.Zeroable.html)
* And all of the above must also hold for any members of the struct.

> [!Warning]
> rust-gpu does not verify that your declarations actually implement `Pod`

Since the CPU crate `mygraphics` depends on our GPU crate `mygraphics-shaders`, we can use our `ShaderConstants` type in our CPU code. Here is an example constructing our `ShaderConstants`, converting them to a `&[u8]` and creating a new `wgpu` buffer with them:

```rust
let shader_constants = ShaderConstants {
time: get_time(),
speed: 0.2,
offset: Vec2::new(0., 0.),
};
let contents = bytemuck::bytes_of(&shader_constants);
let buffer = device.create_buffer_init(&BufferInitDescriptor {
label: Some("ShaderConstants"),
usage: BufferUsages::STORAGE,
contents,
});
```

You can then create a wgpu bind group / vulkan descriptor set with said buffer and submit some work to the GPU with our shader, not detailed here as we assume you to already be familiar with graphics APIs.
Loading
Loading