Skip to content

rust-gpu v0.7

Compare
Choose a tag to compare
@eddyb eddyb released this 21 Apr 21:04
· 100 commits to main since this release

As we keep following Rust's release cadence, it's time for a new release of rust-gpu! Our project aimed at making Rust a first class language and ecosystem for GPU programming. You can read more about why we at Embark started this project in the original announcement.

For this version, the nightly has been updated to nightly-2023-03-04, so make sure to update your rust-toolchain file if you want to switch to version 0.7.0. This nightly includes the Rust language features supported by Rust 1.69 released this week.

As usual, you can find a complete list of changes in the changelog, but keep reading for the highlights.

Error reporting overhaul

As some rust-gpu errors are deferred to reduce false positives (e.g. for unsupported Rust core APIs, if they're not used in a shader), they could vary a lot more in style or precision, from the Rust compiler errors you might expect.

In this release, we've reworked how such errors are reported, to more precisely locate them, and always use the same strategy (regardless of whether the error comes from libraries like core, or shader code itself).

Here's an example of what it can look like, based on one of our tests most impacted by the change:

Previously (up to 0.6) This release (0.7)
image image

SPIR-🇹 untyped/"quasi" pointer (qptr) experiment

One of the biggest difficulties in compiling Rust to SPIR-V, has been the "typed" vs "untyped" memory distinction:
Rust expects that it can freely reinterpret memory (from unsafe Rust, like union or transmute, or even for safe features like enums), while shader languages and SPIR-V require typed memory (with their types limited to arrays and structs).

So far we've only been able to support e.g. only simple Rust enums like Option<usize>, by relying on them acting more like (bool, usize), than the full general case of tagged unions.

With SPIR-🇹 as an intermediate stage between rust-gpu and SPIR-V, however, we can take a new approach: we can extend SPIR-V with our own untyped pointers, and introduce passes for lowering to such pointers (erasing redundant type information), and for lifting from them back to SPIR-V (regenerating the minimal amount of type information) - everything in between can remain faithful to the untyped memory paradigm Rust prefers.
(the lowering part may become unnecessary, if or when rust-gpu eventually starts emitting SPIR-🇹 directly)

The EmbarkStudios/spirt#24 pull request begins this experiment (codenamed qptr, for "quasi"-pointer), and it's included in this release of rust-gpu, but not enabled by default for now (you can also read more about the overall strategy in that PR).

While we've focused for now only on avoiding regressions in existing rust-gpu shaders, this approach is a promising avenue for unlocking, in the long term, Rust features such as enums, slices, or even dynamic allocation - but perhaps more importantly, future rust-gpu releases could unlock parts of the Rust ecosystem never meant to run on GPUs, and we're very excited to see how far we can push such experiments.

For now, if you would like to try out the initial stage of this experiment, you can use:

RUSTGPU_CODEGEN_ARGS="--no-infer-storage-classes --spirt-passes=qptr"

(--no-infer-storage-classes disables the existing rust-gpu "SPIR-V Storage Class inference" pass, meaning the pass lifting qptrs back to SPIR-V pointers, would alone be figuring out all all of the same information)

Whether it works or not, we'd love to hear back! (especially if you encounter bugs with it, that our testing didn't catch).

Improvements to the Image API

We also made some quality-of-live improvements to spirv-std, specifically to the Image API. Previously, most sampling functions always returned a 4-vector, regardless of the image format. With this PR, it will take into account the image format. For example, calling sample() on an rg32f image will yield a glam::Vec2, as the pixel format only has 2 components. For images with an Unknown format, you have to option to specify the number of components, like so: Image!(2d, type=f32, components=3). The components parameter is optional, and defaults to 4 when not specified.

This release also enables a new experimental API to specify what is known as image operands in SPIR-V. We already supported quite a few of these operands by manually specific functions, such as sample_by_lod() that allows you to specify a lod parameter. But with this new API, this is generalized using a builder pattern to build a parameter object that you can then pass to a generic function, like so:

let s = image.sample_with(*sampler, coords, sample_with::bias(1.0));

We currently support the following operands: bias, lod, grad and sample_index. Although the API lets you chain parameters, as in sample_with::bias(1.0).grad(dx, dy).lod(1.0), this combination of parameters do not make sense semantically so you will get a validation error. More operands will be added in the future. One thing that is possible with this new API that wasn't possible before, is specifying the sample index when fetching from a multisampled texture, using image.fetch_with(coord, sample_with::sample_index(idx)). We refer you to the actual PR for more information and usage patterns.