Releases: EmbarkStudios/rust-gpu
rust-gpu v0.9
As we keep following Rust's release cadence, it's time for another 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 Rust-GPU 0.9.0
, the Rust nightly version has been updated to nightly-2023-05-27
,
so make sure to update your rust-toolchain.toml
file when upgrading to Rust-GPU 0.9.0
.
This Rust nightly is equivalent (in language and library features) to the stable Rust 1.71.0
version, released recently.
As usual, you can find a complete list of changes in the changelog, but keep reading for the highlights.
panic!
at the GPU
panic!
s then (Rust-GPU 0.8.0
and earlier)
As many Rust APIs (or even language features) have some edge cases which they handle by panicking (e.g. bounds-checked indexing), Rust-GPU has had some panic!(...)
support for a long time, but it was both unsound, and unhelpful to users:
- unsound, because in Rust-GPU
0.8.0
(and earlier)panic!(...)
was turned intoloop {}
the SPIR-V standard doesn't seem to allow it, but SPIR-V tools/drivers appear to be making the same mistake LLVM originally did, i.e. assuming that such "unproductive" infinite loops can never happen, and makingloop {}
UB (see rust-lang/rust#28728 - UB in safe Rust due to the C-specific assumptions in LLVM, fixed much later by making those assumptions opt-in as "mustprogress
") - unhelpful, because even if the
loop {}
wasn't optimized out, the best case scenario was a timeout error
but on some platforms/drivers, timeouts only work well for compute, or even require using a compute-only queue (via Vulkan, e.g.wgpu
doesn't allow queue selection yet) to avoid disrupting the rest of the user's system (as compositors and other applications may be slowed down or even hanged into a crash by a timeout on one of the graphical/mixed queues, if they're blocked behind theloop {}
-ing shader)- crucially, neither "which
panic!
", nor "which shader invocation" (caused thepanic!
) could be determined
- crucially, neither "which
panic!
s now (Rust-GPU 0.9.0
)
PRs #1070, #1080, #1036, #1082 replaced the old strategy (and refined the new one), resulting in:
-
by default, a
panic!(...)
is now soundly propagated all the way up to the shader entry-point, as if the Rust code had used-> Result<_, ()>
and?
after every call (and so acts as a silent "early exit" for invocations that trigger it) -
spirv_builder::ShaderPanicStrategy
allows further customizing the behavior, notably:SpirvBuilder::new(/* ... */) .shader_panic_strategy(ShaderPanicStrategy::DebugPrintfThenExit { print_inputs: true, print_backtrace: true, })
enables
debugPrintf
with maximal detail:panic!
message + shader "inputs" + (compile-time) "backtrace"
(seeShaderPanicStrategy
docs for more details, and also how to e.g. enabledebugPrintf
output from Vulkan) -
panic!(...)
messages support (some) formatting, by conversion to an equivalentdebugPrintf
(e.g.assert!(x < 1.0, "{x} is not sub-unitary")
will printx
's value usingdebugPrintf
's%f
specifier)
Here's what it looks like in practice to get panic!(...)
messages from the GPU:
Specialization Constants
SPIR-V allows shaders to declare "specialization constants" (i.e. OpSpecConstant
) which can have their values specified just before using the shader module in e.g. a Vulkan pipeline (WebGPU also has this feature, calling it "pipeline-overridable constants").
While some shader languages can choose to expose this feature as a variation on their equivalent of "compile-time constants" (e.g. layout(constant_id = 123) const T x;
in GLSL), Rust's const
s and const
-generic parameters are required to be known by the Rust compiler, so they're not a plausible path for Rust-GPU to expose this SPIR-V feature.
Instead, we're exposing the feature via #[spirv(spec_constant(...))]
-decorated shader inputs (only u32
for now):
#[spirv(vertex)]
fn main(
// Default is implicitly `0`, if not specified.
#[spirv(spec_constant(id = 1))] no_default: u32,
// IDs don't need to be sequential or obey any order.
#[spirv(spec_constant(id = 9000, default = 123))] default_123: u32,
) {...}
You can read more about this feature in the "Specialization constants" section in the Rust-GPU book (which goes into more detail about the background of this feature, and Rust/Rust-GPU-specific limitations).
rust-gpu v0.8
As we keep following Rust's release cadence, it's time for another 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 Rust-GPU 0.8.0
, the Rust nightly version has been updated to nightly-2023-04-15
,
so make sure to update your rust-toolchain.toml
file when upgrading to Rust-GPU 0.8.0
.
This Rust nightly is equivalent (in language and library features) to the stable Rust 1.70.0
version, released this week.
As usual, you can find a complete list of changes in the changelog, but keep reading for the highlights.
The SPIR-🇹 shader IR framework is now mandatory
Our SPIR-🇹 shader IR framework started with the goal of allowing us to work with GPU shaders beyond the limitations of manipulating them in their standard SPIR-V form (which is what Rust-GPU has been doing). Back in Rust-GPU 0.4.0
, we introduced it as opt-in (behind a --spirt
flag), and in Rust-GPU 0.6.0
it became enabled by default (with a --no-spirt
opt-out).
While the role it plays in Rust-GPU is still minimal by default (with some experiments behind flags), almost all our plans for improving Rust-GPU's support for the Rust language (such as the qptr
experiment) revolve around relying more and more on it, for every step necessary to map Rust semantics onto GPU shader (SPIR-V) semantics.
With this focus on SPIR-🇹 in mind, and to avoid the maintenance cost of duplicate codepaths and testing (not to mention an eventual feature parity gap), we've decided to remove --no-spirt
, making SPIR-🇹 mandatory going forward.
How may users be impacted by the removal of --no-spirt
?
The most obvious reason one would've used --no-spirt
in previous Rust-GPU versions is to work around bugs or incompatibilities unique to SPIR-🇹, which, while entirely possible, is not something we are aware of being done in practice.
If you do encounter such bugs, please report them as a Rust-GPU GitHub issue (and/or as a SPIR-🇹 issue, if the nature of the bug is unambiguous).
But more commonly, --no-spirt
has been used by wgpu
users to work around naga
issue #1977.
wgpu
uses naga
internally for shader language (and SPIR-V) support, and Rust-GPU loops may cause it to error with:
Shader validation error:
┌─ :1:1
│
1 │
│ naga::Function [1]
Function [1] '' is invalid
The `break` is used outside of a `loop` or `switch` context
While --no-spirt
didn't guarantee naga
would behave correctly, it did work in more cases than with SPIR-🇹 enabled.
(this was due to some differences in how structured control-flow is represented - SPIR-V supports both while
and do
-while
loops, but naga
only implemented while
loops initially, and SPIR-🇹 always emits do
-while
loops)
That naga
bug has since been fixed, and the fix is present in:
naga 0.11.1
, for users ofwgpu 0.15
naga 0.12.1
, for users ofwgpu 0.16
- future
naga
versions (starting with0.13
, to be used by futurewgpu
versions)
If you've been using --no-spirt
to work around this naga
bug, you should be able to update to one of the above versions by using cargo update -p naga
, and you can search for "naga"
in Cargo.lock
(or cargo tree
) to check its version.
Further error reporting improvements
Rust-GPU 0.7.0
improved error reporting in some respects, but there were still limitations in how much detail we could record in SPIR-V to be used for error reporting. We've since started using a custom "extended instruction set" (SPIR-V OpExtInst
) to give us additional flexibility.
The main improvement is in replacing the basic standard SPIR-V file:line:col
debuginfo (OpLine
), with a custom one that supports source location ranges as well (which is normally expected of all Rust diagnostics).
Using the same example as the Rust-GPU 0.7.0
release notes:
old (standard) approach | new (custom) approach (0.8.0 ) |
---|---|
We've also added support to our inliner to generate custom debuginfo, to distinguish inlined callee code, from the surrounding caller code. While most errors are reported before any functions are inlined, all SPIR-🇹 passes currently run after the inliner, and in the future diagnostics could ideally be reported at the very end (to maximize the chances of the underlying issue to be legalized away, instead of being reported as an error to the user).
This is the same example, but with RUSTGPU_CODEGEN_ARGS="--no-early-report-zombies"
(which delays some reporting):
without any inliner debuginfo support | with inliner debuginfo support (0.8.0 ) |
---|---|
For the first error, all functions were inlined and "called by" didn't even show up in the diagnostics at all, but now it does. There are still some differences between the two errors, but all the useful information should always be present now.
rust-gpu v0.7
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 ) |
---|---|
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 enum
s), while shader languages and SPIR-V require typed memory (with their types limited to arrays and struct
s).
So far we've only been able to support e.g. only simple Rust enum
s 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 enum
s, 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 qptr
s 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.
rust-gpu v0.6.1
A new minor release containing the fix (#1006) for a bug (#1002) causing incorrect SPIR-V to be generated when issuing a OpReturnValue
instruction inside an asm!
block of a function that gets inlined.
Affected API functions of spirv-std
are:
IndexedUnchecked::index_unchecked()
for[T]
and[T; N]
IndexedUnchecked::index_unchecked_mut()
for[T]
and[T; N]
AccelerationStrucutre::from_u64()
AccelerationStructure::from_vec()
RuntimeArray::index()
RuntimeArray::index_mut()
This bug got triggered by a new inlining codepath in rustc's MIR and was introduced somewhere before the release of 0.4.0
and remained undetected until now.
A workaround for previous versions of rust-gpu is to compile your shaders without optimizations in general, e.g. by calling release(false)
on your SpirvBuilder
object, or by making sure the following environment variable is set as such: RUSTGPU_RUSTFLAGS=-Zmir-opt-level=0
rust-gpu v0.6
Rust-gpu hot reloading in action using the rust-gpu plugin for Bevy
Hi there, and welcome to another 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-01-21
, so make sure to update your rust-toolchain
file if you want to switch to version 0.6.0
. This nightly includes the Rust language features supported by Rust 1.68 released last week.
Image API
This release also contains the first part of a change to the Image
API, namely the removal of return type inference for the texture fetch functions. In a nutshell, because of the return type inference, the Rust compiler had to know the type where you store the result of a texture fetch. For most use cases, this implied that you had to specifically store the result in an explicitly typed variable before being able to do further computations on it, like so:
// You had to do this in 0.5.0 and before:
let s: Vec3 = texture.sample(uv);
let result = s.xy() * coefficients;
// Rather than this (this works starting from 0.6.0):
let result = texture.sample(uv).xy() * coefficients;
By removing the return type inference, the texture fetch functions, such as sample()
, always return a concrete glam
Vector type (typically a glam::Vec4
for unknown and floating point formats). However, in order to do this, we had to make the API less generic, and therefore remove support for user supplied vector library bindings. This also means that glam
has now become a non-optional dependency of spirv-std
, and you no longer need to add glam
as feature when depending on spirv-std
(in fact, you'll need to remove glam
if specified as feature).
If you were using your own vector library, then unfortunately this is a breaking change. You'll need to either switch to glam
, or manually convert to and from your vector type of choice. We may want to add support for other popular vector libraries in the future.
Code generation
Furthermore, we made the previously experimental shader IR framework SPIR-🇹 the default. Extensive testing has not resulted in any issues or performance degradation, and in fact this may improve the generated SPIR-V code of your shader. If you previously already opted in using --spirt
in the codegen args, you must remove the flag. On the other hand, if this change is causing you issues, you can opt out by specifying --no-spirt
(and please let us know by filing a new issue 🙏). You can do so in the RUSTGPU_CODEGEN_ARGS
environment variable, or using the new extra_arg()
function when invoking the SpirvBuilder
from code.
Community spotlight
A big shoutout to the developers of Bevy, and @Shfty in particular, for adding support for rust-gpu
to Bevy
. The above title image shows hot reloading of shaders written in rust-gpu
in the engine. Awesome work, and much appreciated for fast prototyping of rust-gpu
shaders! 🚀
rust-gpu v0.5
Yes that's right, another rust-gpu
release! 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.
As we mentioned in the previous release notes, we intended to do more frequent releases, and more or less in line with new stable Rust releases. That way you can use your beloved new Rust features as soon as possible. As Rust 1.67 was released last week, it's time for a rust-gpu update as well.
Not much has changed as the 0.4
release was only a month ago. The most notable changes are the update to the nightly-2022-12-18
toolchain, which should cover all new features new to Rust 1.67. We also made it compatible with Rust edition 2021, while we required 2018 before. As usual, more information in the changelog.
That said, we have been busy with some exciting stuff behind the scenes, as we're focusing on better SPIR-V generation and quality of life improvements to the spirv-std
API that'll hopefully make writing shaders in rust-gpu
a bit less cumbersome in some situations.
Contributors
Thank you to all the contributors who helped make this release possible! 🎉
@Bobo1239, @eddyb, @oisyn.
Special shoutout to @Bobo1239 for submitting their first contribution 🥳.
rust-gpu v0.4
Screenshot from Embark's Creative Playground, which uses rust-gpu and raytracing.
Hi there! It's been long overdue, but it's finally here: the fourth 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.
With this release, all relevant crates have been published to crates.io. In fact, this has been the case since 0.4.0-alpha.13
, released last August. So you no longer have to refer to our github to pull in rust-gpu
, and can instead directly refer to spirv-std
and spirv-builder
by version! What hasn't changed, though, is that you still need to build your shaders using a specific nightly toolchain of Rust. For rust-gpu 0.4.0
, this will be nightly-2022-10-29
. We do produce a user friendly error message when the toolchain you're building with does not exactly match the version required for rust-gpu
.
It has been a while since we published a proper release, and going forward we'd like to do better. We intend to keep rust-gpu
on the same schedule as the stable Rust release, so you can use your favorite new language features as new stable versions of Rust are being released, by just updating your rust-gpu
version. As of right now, rust-gpu
uses a Rust nightly that is equivalent to Rust 1.66
, and you can expect a new release on or around the 26th of January, 2023, when Rust 1.67
is planned to be released.
A lot has changed since v0.3, too much to summarize in these release notes. However, we have started to track our changes in a changelog and have even retroactively listing all changes since the first release 🙂. We'll try our best to feature some of the hightlights here, but please refer to the changelog for a more detailed accounting.
There is one thing we want to highlight in particular: rust-gpu
now supports ray-tracing. Using #[spirv(..)]
attributes, you can define entry points for various raytracing events: intersection
, any_hit
, closest_hit
, and miss
. The #[spirv(ray_generation)]
can be used to define a ray generation shader. We still lack proper examples in our codebase, but for now we can refer you to the spirv_std::ray_tracing
documentation. The code example below shows a somewhat stripped down shader that is used to generate the shadows in above screenshot.
Show ray-tracing code example
pub struct ShadowPayload {
shadowed: u32,
}
pub fn trace_shadow_ray(
acceleration_structure: &AccelerationStructure,
mut ray_desc: RayDesc,
payload: &mut ShadowPayload,
) -> bool {
payload.shadowed = 1;
unsafe {
// see documentation of `spirv_std::ray_tracing::AccelerationStructure` on what these values mean
acceleration_structure.trace_ray(
RayFlags::SKIP_CLOSEST_HIT_SHADER, // ray flags
!0, // cull mask
0, // shader binding table offset
0, // shader binding table stride
0, // miss index
origin, // ray origin
0.0, // tmin
direction, // ray direction
f32::MAX, // tmax
payload, // payload
);
}
payload.shadowed != 0
}
#[spirv(miss)]
pub fn miss(#[spirv(incoming_ray_payload)] payload: &mut ShadowPayload) {
payload.shadowed = 0;
}
#[spirv(ray_generation)]
pub fn raygen(
#[spirv(descriptor_set = 0, binding = 0)] output_tex: &Image!(2D, format=rgba16f, sampled=false),
#[spirv(descriptor_set = 1, binding = 0)] acceleration_structure: &AccelerationStructure,
#[spirv(ray_payload)] payload: &mut ShadowPayload,
) {
let ray_origin = /* calculate ray origin */;
let ray_dir = /* calculate ray dir */;
let shadowed = trace_shadow_ray(
acceleration_structure,
ray_origin,
ray_dir,
payload,
);
unsafe {
output_tex.write(px, if shadowed { Vec3::ZERO } else { Vec3::ONE });
}
}
Language and code generation changes
- Updated toolchain to
nightly-2022-10-29
. - Changed the way the
#[spirv(..)]
attribute works, because of the removal ofregister_attr
support in Rust. You now need thespirv
macro attribute to be globally visible by doing:See also this migration guide for more information. PR#926use spirv_std::spirv;
- Replaced
spirv_std::storage_class
"named pointer types" with#[spirv(...)] &T
entry-point parameters. (PR#443). This means you can now use the following code:#[spirv(fragment)] pub fn main( #[spirv(frag_coord)] in_frag_coord: &Vec4, // instead of: #[spirv(frag_coord)] in_frag_coord: Input<Vec4>, output: &mut Vec4, // instead of: mut output: Output<Vec4>, ) { // ... }
- Added basic support for unsized structs (e.g. ending with a
[T]
field). PR#504 - Removed the need to manually specify the storage class for
Image
/Sampler
/ImageSampler
entry-point parameters. PR#567 - Added support for constant memory (
&'static _
references), within the limits of SPIR-V. PR#586 - Added
#[spirv(subgroup_local_invocation_id)]
. PR#848 - Removed the
fn
/closure#[spirv(unroll_loops)]
attribute. You can use#[spirv(unroll)]
on individual code blocks instead. PR#946 - This release includes an experimental new shader IR framework called
SPIR-🇹
, which will ultimately help us in generating better SPIR-V code. This library is opt-in, see here how to enable it. Again, experimental, use at own risk. PR#940
API changes
spirv-std
- Added a
const
-genericImage
type, andImage!
macro wrapping it. PR#359.#[spirv(fragment)] pub fn main( #[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled), #[spirv(descriptor_set = 1, binding = 1)] image_array: &Image!(2D, type=f32, arrayed, sampled), #[spirv(descriptor_set = 2, binding = 2)] cubemap: &Image!(cube, type=f32, sampled), #[spirv(descriptor_set = 3, binding = 3)] sampler: &Sampler, output: &mut f32, ) { let v2 = glam::Vec2::new(0.0, 1.0); let v3 = glam::Vec3A::new(0.0, 0.0, 1.0); *output = image.sample_depth_reference(*sampler, v2, 1.0); *output += image_array.sample_depth_reference(*sampler, v3, 1.0); *output += cubemap.sample_depth_reference(*sampler, v3, 1.0); }
- As you might have noticed from above example, cubemaps are now supported as well. PR#521
- Float packing/unpacking operations. PR#709
- Lots of other new APIs
Image:Image::gather
/Image::sample_bias
#704,Image::query_*
#608,Image::read_subpass
#643,Image::sample_by_lod
/Image::sample_by_gradient
#498,Image::fetch
#480
arch:arch::read_clock_khr
#757,arch::{signed,unsigned}_{min,max}
#763,arch::*memory_barrier*
#769,arch::IndexUnchecked
#805,arch::atomic_i_increment
#839,arch::atomic
#877
Misc:ByteAddressableBuffer
#735,SampledImage::sample_by_lod
#755,debug_printf!
#768,RayQuery::confirm_intersection
#822,is_helper_invocation
#612,memory_barrier
/control_barrier
#519
spirv-builder
- You can now have multiple SPIR-V modules, one per entry point. PR#551
SpirvBuilder
now supports the ability to set a Rust "target triple" (e.g."spirv-unknown-vulkan1.1"
for Vulkan1.1
). PR#559- Added the ability to query entry-point names. [PR#622](https://github.com/EmbarkStudios/rust-gpu/...
rust-gpu v0.3
The Khronos Sci-Fi helmet model. glTF model viewer created by @msiglreith using rust-gpu
(Source)
Hello everyone, and welcome to the third 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.
We're still not publishing releases to crates.io or distributing artefacts for rust-gpu
, but we still wanted to highlight some of the changes that have been happening over the past couple of months. For more information on how to get started with using rust-gpu
in your projects, be sure to check out the Rust-GPU Dev Guide! You can always add a git dependency in cargo to access spirv-builder
.
[build-dependencies]
spirv-builder = { git = "https://github.com/EmbarkStudios/rust-gpu.git", branch = "main" }
While not a user facing feature, we are also proud to announce that we've also reached an important internal milestone in rust-gpu
. We've now completely rewritten our GLSL shaders in our internal Ark project with Rust thanks to the improvements in this release! 🎉
rustc_codegen_spirv
- The biggest change to
rust-gpu
is the added ability to infer storage class types. This removes the need to call.load/.store
on the storage class types, and allows you to useDeref/DerefMut
to manipulate the inner type directly. This a huge improvementrust-gpu
ergonomics, as well leading to more efficient code being generated with the removal of unneededOpLoad
s andOpStore
s when using large types.use spirv_std::{glam::{Vec4, vec2}, storage_class::{Input, Output}}; #[spirv(fragment)] pub fn main_fs( #[spirv(frag_coord)] in_frag_coord: Input<Vec4>, mut output: Output<Vec4>, ) { // Before let frag_coord = frag_coord.load(); output.store(black_box(vec2(frag_coord.x, frag_coord.y))); // Now *output = black_box(vec2(in_frag_coord.x, in_frag_coord.y)); }
- Initial support for Algebraic Data Type enums (e.g.
Option<T>
) has been added. This support is initially only provided for enums which have all scalar values in each variants (integers, floats, booleans). So for exampleOption<u32>
works, butOption<Struct>
doesn't work at the moment. - You no longer need add
#[allow(unused_attributes)]
in front of#[spirv]
attributes to remove warnings. - You can now provide
const
arguments toasm!
. rustc_codegen_spirv
will now remove differentOpName
s that target the same ID. This should reduce the size of the generated SPIR-V binaries.rustc_codegen_spirv
will now try to deduplicate generatedOpVariable
s. This fixes certain issues with compiling rust-gpu SPIR-V on Android platforms.- You can now set
entry_point_name
in entry point attributes to change the final name of an entry point. E.g.#[spirv(vertex(entry_point_name = "foo_bar"))]
- You can now add the
#[spirv(unroll_loops)]
attribute to functions, which tellsrustc_codegen_spirv
to annotate all loops inside withUnroll
.
spirv-std
- Added a new
arch
module which provides an abstraction some basic SPIR-V instructions as free functions. The first iteration includes two functions.vector_extract_dynamic
(OpVectorExtractDynamic
)vector_insert_dynamic
(OpVectorInsertDynamic
)
- Added
textures::StorageImage2d
which is an image designed be read/written to without aSampler
. - Added the
spirv-std-macros
crate for holding thespirv
proc macro which acts as dummy macro to allow to use thespirv
attribute on CPU code (has no affect on CPU code). - Added the
gpu_only
proc macro, which allows you to mark a function as only runnable on GPU, it will still generate a callable function on the CPU side, but it will cause a panic when called. - Added
discard
anddemote_to_helper_invocation
functions. Which correspond todiscard
in GLSL and HLSL respectively. Derivative
is now implemented forglam::{Vec2, Vec3, Vec3A, Vec4}
.- Added the
SampledImage
type, which is an image already combined with a sampler.
spirv-builder
- You can now build your shaders in
release
mode with--release
or withSpirvBuilder::release
- If you're using
spirv-builder
in your build script, you can add the following to yourCargo.toml
to improve build times.[profile.dev.build-override] opt-level = 3 [profile.release.build-override] opt-level = 3
Contributors
Thank you to all the contributors who helped make this release possible! 🎉
rust-gpu v0.2
Created by @bcata6 using rust-gpu
(Rust source) with shaders originally from https://shadertoy.com/
Hello everyone, and welcome to the second 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.
First of all we'd like to welcome @eddyb, who will be working as a collaborator with the rust-gpu team on the compiler backend, and has already been contributing a lot of improvements to rust-gpu
!
We're still not really cutting releases for rust-gpu
, but we wanted to highlight some of the changes that have been happening over the past month. For more information on how to get started with using it in your projects, be sure to check out the new Rust-GPU Dev Guide!
Community
- SHADERed, an IDE designed for building shaders have released a plugin and added support for running
rust-gpu
in SHADERed lite, their in-browser editor. Now you can to write Rust shaders on the web and in an IDE! Check out their blog post for more details on the new features.
SPIR-V Backend
- We've added a new structurizer, which means that you can now use
match
expressions andcontinue
s! - Added the
#[spirv(flat)]
attribute that matches SPIR-V's "Flat" decorator. - Textures are now supported. You can now use the GPU's image memory to sample and manipulate textures.
- Panicking code is now supported.
- Added support for SPIR-V 1.0.
- You can now use procedural macros, such as the
unroll
crate in your shaders. - We've also made some improvements to error messages about constant pointers.
- Thanks to a community contribution we now also have initial support for compute shaders. Though support is still pretty bare bones for now this is an important start towards supporting different shaders in
rust-gpu
. - Added initial support in Rust and
rust-gpu
for inline SPIR-V with theasm!
nightly feature. You can now write inline SPIR-V in your Rust code (example below). This is an important step towards implementing a lot of functionality around SPIR-V's features, such as the aforementioned new texture support.asm! { "%typeSampledImage = OpTypeSampledImage typeof*{1}", "%image = OpLoad typeof*{1} {1}", "%sampler = OpLoad typeof*{2} {2}", "%coord = OpLoad typeof*{3} {3}", "%sampledImage = OpSampledImage %typeSampledImage %image %sampler", "%result = OpImageSampleImplicitLod typeof*{0} %sampledImage %coord", "OpStore {0} %result", in(reg) &mut result, in(reg) &image, in(reg) &sampler, in(reg) &coord }
spirv-std
- All Storage Classes (e.g.
Input
/Output
) are now defined inspirv_std::storage_class
. - Rust's language items such
rust_eh_personality
andpanic_handler
are now defined inspirv-std
for SPIR-V targets. This reduces the amount of boilerplate code you need in your shader, and helpsspirv-std
feel a bit more likestd
. You can still define your own language items by disabling thelang_items
feature.
Misc
rust-gpu
now has testing support for building on Android. You can now also run the WGPU example on Android devices to test out your shaders.
Contributors
Thank you to all the contributors who helped make this release possible! 🎉
Introducing rust-gpu v0.1 🐉
Today, we're releasing a very early version of rust-gpu - a new project to make Rust a first-class language and ecosystem for GPU code!
GPU programming has historically been done with HLSL or GLSL, simple programming languages that have evolved along with rendering APIs over the years. However, as game engines have evolved, these languages have failed to provide mechanisms for dealing with large codebases, and have generally stayed behind the curve compared to other programming languages.
Our hope with this project is that we push the industry forward by bringing Rust, an existing low-level, safe, and high performance language, to the GPU. And with it come some additional great benefits: a package/module system that's one of the industry's best, built in safety against race-conditions or out of bounds memory access, a wide range of libraries and tools to improve programmer workflows, and many others!
If we do this project right, developers could use open-source crates that provide the graphical effects needed to create beautiful experiences. Instead of sharing snippets of code on forum posts, they could simply add the right crates.
Why Embark?
At Embark, we've been building our own new game engine from the ground up in Rust. We have previous experience in-house developing the RLSL prototype, and we have a team of excellent rendering engineers that are familiar with the problems in current shading languages both from games, game engines and other industries. So, we believe we are uniquely positioned to attempt solving this problem.
We want to streamline our own internal development with a single great language, build an open source graphics ecosystem and community, facilitate code-sharing between GPU and CPU, and most importantly: to enable our (future) users, and fellow developers, to more rapidly build great looking and engaging experiences.
Who are we?
- Leading the project:
- @khyperia - compiler engineer with experience from Microsoft & Mozilla
- @Jasper-Bekkers - rendering engineer with experience from Frostbite & SEED, and head of Traverse Research
- @XAMPPRocky & @arirawr, will be helping to manage the open development of the project.
- @VZout, @repi, @MaikKlein, @h3r2tic, @hrydgard, @emilk, @NiklasNummelin - devs that will try to write all their shaders in Rust!
But we can't do this alone. That's why we've decided to develop this project in the open,alongside the rest of the Rust and graphics programming communities. Please connect with us if you are interested in collaborating
Current Status
Compiling and running very simple graphics shaders works, and a significant portion of the core library also compiles.
With this we've been able to create some first real shaders in Rust, such as this sky shader written (source):
However, many things aren't implemented yet: for example, loops and switches aren't supported yet!
That means that while being technically usable, this project is far from being production-ready.
Project Scope
The scope of this overall project is quite broad, but is in multiple stages
rustc
compiler backend to generate SPIR-V, plugging in via-Z codegen-backend
.- This is the same mechanism that rustc_codegen_cranelift and rustc_codegen_gcc use.
- Currently only SPIR-V support is planned, Vulkan's open compiler target
- Possible a future version could suppport DXIL (the target for DirectX) or WGSL (the WebGPU shading language that's bijective with SPIR-V)
- Focus on Vulkan graphics shaders first, then after Vulkan compute shaders
- Cargo and crates.io support to develop and publish SPIR-V crates
- High-level render graph to take advantage of this, make it easy for users to develop and use rendering effects.
An in-depth exploration of our roadmap and milestones can be found here.
Open Development
We'll have weekly meetings on Discord, with open agendas shared in GitHub issues. RFCs and other discussions will also be handled in GitHub.
Want to get started?
- Join us on Discord to discuss and share your ideas on this project
- Check out our Roadmap and Milestones
- Read about the project's scope
- Get started and run our example shader
- See our open issues