Code Monkey home page Code Monkey logo

rust-gpu's Introduction

๐Ÿ‰ rust-gpu

Rust as a first-class language and ecosystem for GPU graphics & compute shaders

Embark Discord Documentation dependency status Build status

Current Status ๐Ÿšง

Note: This project is still heavily in development and is at an early stage.

Compiling and running simple shaders works, and a significant portion of the core library also compiles.

However, many things aren't implemented yet. That means that while being technically usable, this project is far from being production-ready. Support for specific features in Rust and SPIR-V are tracked on GitHub.

Example

Sky shader

use glam::{Vec3, Vec4, vec2, vec3};

#[spirv(fragment)]
pub fn main(
    #[spirv(frag_coord)] in_frag_coord: &Vec4,
    #[spirv(push_constant)] constants: &ShaderConstants,
    output: &mut Vec4,
) {
    let frag_coord = vec2(in_frag_coord.x, in_frag_coord.y);
    let mut uv = (frag_coord - 0.5 * vec2(constants.width as f32, constants.height as f32))
        / constants.height as f32;
    uv.y = -uv.y;

    let eye_pos = vec3(0.0, 0.0997, 0.2);
    let sun_pos = vec3(0.0, 75.0, -1000.0);
    let dir = get_ray_dir(uv, eye_pos, sun_pos);

    // evaluate Preetham sky model
    let color = sky(dir, sun_pos);

    *output = tonemap(color).extend(1.0)
}

See source for full details.

Our Vision & Community Contributions

rust-gpu is a project that we at Embark think has the potential to change the way GPU programming works in multiple ways. One of the primary things we think it can change is opening the door to leverage the open source culture of sharing and improving each others' code, and our end goal and vision for rust-gpu is to develop it very much in tandem with the community. However, the project is still in quite early stages and has a very small team working on it, so in order to be productive and guide the project to where we ultimately want it to go, as of right now, we need to focus on our own primary use cases for our projects at Embark.

What this means practically is that it is unlikely that we'll be able to accept major changes from community members at this time. If you have a large change you would like to make, please file an issue and/or ask on our Discord in the #rust-gpu channel to see if it is something we'll be able to accept before working on it, as it is not great to have to turn down stuff that community members have poured their time and effort into. As the project matures, we'll in theory be able to accept more input from the community and move closer and closer to the goals outlined above. Thank you so much for your understanding!

Getting started

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

Experiment with rust-gpu shaders in-browser at SHADERed.

Background

Historically in games GPU programming has been done through writing either HLSL, or to a lesser extent GLSL. These are 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.

In part this is because it's a niche language for a niche market, and in part this has been because the industry as a whole has sunk quite a lot of time and effort into the status quo. While over-all better alternatives to both languages exist, none of them are in a place to replace HLSL or GLSL. Either because they are vendor locked, or because they don't support the traditional graphics pipeline. Examples of this include CUDA and OpenCL. And while attempts have been made to create language in this space, none of them have gained any notable traction in the gamedev community.

Our hope with this project is that we push the industry forward by bringing an existing, low-level, safe, and high performance language to the GPU; namely Rust. And with it come some additional benefits that can't be overlooked: 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 tools and utilities to improve programmer workflows, and many others!

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.

If we do this project right, one wouldn't necessarily need an entire team of rendering engineers to build a good looking game, instead one would simply use a few of the existing open-source crates that provide the graphical effects needed to create the experience you're after. Instead of sharing and copy'n'pasting snippets of TAA code on forum posts, one could simply find and use the right crates from crates.io.

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.
  • 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.

Process

We use this repo as a monorepo for everything related to the project: crates, tools, shaders, examples, tests, and design documents. This way, we can use issues and PRs covering everything in the same place, cross-reference stuff within the repo, as well as with other GitHub repos such as rspirv and Rust itself.

We meet weekly over a Discord call to discuss design and triage issues. Each meeting has an issue with agenda, links and minutes.

We have a #rust-gpu Discord channel for fast discussion and collaboration.

Backwards compatibility, breaking changes and deprecation

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 for in-depth discussion and specs.
  • rustc_codegen_spirv for the compiler itself.
  • spirv-std for GPU intrinsics, types, and other library items used by GPU 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.

  • 2016: glassful Compiles a subset of Rust to GLSL.
  • 2017: inspirv-rust Experimental Rust to SPIR-V compiler.
  • 2018: nvptx Rust to PTX compiler.
  • 2020: accel GPGPU library for Rust.
  • 2020: rlsl Predeccesor to rust_gpu, Rust to SPIR-V compiler.
  • 2021: Rust CUDA Rust to PTX compiler, similar mechanism to rustc_codegen_spirv.

Contributing

Contributor Covenant

We welcome community contributions to this project.

Please read our Contributor Guide for more information on how to get started.

License

Licensed under either of

at your option.

Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.

rust-gpu's People

Contributors

arirawr avatar beastle9end avatar bjorn3 avatar charles-r-earp avatar deanbdean avatar dependabot[bot] avatar dgriffin91 avatar djmcnab avatar dvc94ch avatar eddyb avatar electronicru avatar expenses avatar fornwall avatar fu5ha avatar hatoo avatar hentropy avatar hrydgard avatar jake-shadle avatar jasper-bekkers avatar jnises avatar khyperia avatar lpil avatar marijns95 avatar molikto avatar msiglreith avatar nipung314 avatar oisyn avatar repi avatar vzout avatar xampprocky avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

rust-gpu's Issues

Pseudo port of a simple lighting shader

Can move this to markdown files instead if this doesn't fit in the issue tracker. However maybe it's a nice place to talk around these.

Current glsl shader

struct SurfaceParams {
    vec3 diffuse_color;
    vec3 world_position;
    vec3 world_normal;
};

struct EnvParams {
    vec3 sun_world_direction;
    vec3 sun_color;
    vec3 ground_color;
    vec3 sky_color;
    vec3 horizon_color;
};

struct ViewParams {
    vec3 camera_world_position;
};

vec3 ark_light(in SurfaceParams surface, in ViewParams view, in EnvParams env) {
    vec3 L = normalize(env.sun_world_direction);
    vec3 N = normalize(surface.world_normal);
    vec3 V = normalize(view.camera_world_position - surface.world_position);
    vec3 H = normalize(V + L);

    float LdotN = max(0.0, dot(N, L));
    float HdotN = max(0.0, dot(H, N));
    float specular = pow(HdotN, 80.0);

    // Microfacet fresnel.
    float fresnel = pow(max(0.0, 1.0 - dot(V, H)), 5.0);

    // Assuming the common IOR of 1.4, F0 is around 0.04.
    vec3 specular_color = env.sun_color * mix(0.04, 1.0, fresnel);

    // Do a two part gradient, from ground to horizon to sky, to match our
    // current simplistic sky model.
    vec3 ambient_light;
    if (N.y < 0.0) {
        ambient_light = mix(env.horizon_color, env.ground_color, -N.y);
    } else {
        ambient_light = mix(env.horizon_color, env.sky_color, N.y);
    }

    vec3 diffuse_light = LdotN * env.sun_color;
    vec3 specular_light = specular * specular_color;
    return (surface.diffuse_color.rgb * (ambient_light + diffuse_light) +
            specular_light);
}

Proof of concept Rust shader

struct SurfaceParams {
    diffuse_color: Vec3,
    world_position: Vec3,
    world_normal: Vec3,
}

struct EnvParams {
    sun_world_direction: Vec3,
    sun_color: Vec3,
    ground_color: Vec3,
    sky_color: Vec3,
    horizon_color: Vec3,
}

struct ViewParams {
    camera_world_position: Vec3
}

fn ark_light(surface: SurfaceParams, view: ViewParams, env: EnvParams) -> Vec3 {
    let l = env.sun_world_direction.normalize();
    let n = surface.world_normal.normalize();
    let v = (view.camera_world_position - surface.world_position).normalize();
    let h = (v + l).normalize();

    let l_dot_n = n.dot(l).max(0.0);
    let h_dot_n = h.dot(n).max(0.0);
    let specular = h_dot_n.pow(80.0);

    let fresnel = (1.0 - v.dot(h)).max(0.0).pow(5.0);

    let specular_color = env.sun_color * lerp(0.04, 1.0, fresnel);

    let ambient_light = if n.y < 0.0 {
        lerp(env.horizon_color, env.ground_color, -n.y)
    } else {
        lerp(env.horizon_color, env.sky_color, n.y)
    };

    let diffuse_light = l_dot_n * env.sun_color;
    let specular_light = specular * specular_color;

    surface.diffuse_color.rgb * (ambient_light + diffuse_light) +
        specular_light
}

Labelling system

I don't about everyone else, but I find the single word labels that GitHub provides you don't really scale well for larger projects especially once you grow into hundreds or thousands of issues, as you often want to have a number of labels that helps you find issues but not a lot of noise around the labels to the degree that's hard to tell what any of them mean.

To that end I think we should have some categories for our labels that help keep the labels naming and colours more consistent.

Potential Categories

  • Issue Category
    • Bug
    • Feature Request
    • Tracking Issue
    • RFC Proposal
  • Platform
    • Windows
    • macOS
    • Linux
  • Library/Crate
    • rspirv-linker
    • rustc_codegen_spirv
    • spirv-builder
    • spirv-std
    • Meta (README, CI config, etc)
  • PR Status
    • Waiting on Review
    • Waiting on author
    • Blocked (depends on an external factor being resolved, e.g. a PR being merged in rust-lang/rust.)

Meeting 2020-10-01

Previous meeting: #35

Initial agenda (please edit and add)

  • Prototype compiler update - @khyperia
  • Relooper update #24 @VZout
    • Possibly split up the "control flow ops" part of the structurizer? Good first issue?
  • What is needed for our Q4 objective of getting #36 up? - @khyperia
    • biggest rustc_codegen_spirv issues remaining are #31, #42, #43, #44, #45.
      • Some of these aren't required for the Q4 objective, but will be needed soon after anyway.
  • Pod structure and roles - @repi

Ergonomic Rust CPU to Rust GPU / SPIRV bindings

This is a design issue to research and figure out how to the bindings between shaders and our Rust CPU code (native or WASM) should look like. How should resources like buffers, input layouts, samplers, textures be bound and connected between the CPU execution and the GPU dispatch.

A bunch of this work can be done today just based on existing SPIRV shaders, but there likely will be advantages and simplifications once we Rust on both sides, GPU shaders compiling with Rust being bound and dispatched from Rust CPU code, so that can also be discussed.

There is previous work to look into also wrt to SPIRV reflection and Rust binding, like Rendy's descriptor design

Add CI automerge of PRs

Once we open up the project we should add and configure http://mergify.io to get automerging of PRs passing reviews and tests. We use this in all of our open source projects, but it doesn't work well for private projects.

I can do this

Investigate custom `repr` for GPU memory

A few options here:

  • Hack the compiler to just output the data layout that we want
  • Add #[repr(std140)] and #[repr(std430)] to the language frontend (maybe have better names for when we want to support non-Vulkan backends with different layout requirements
  • Support #[repr(Rust)] and #[repr(C)] natively through Vulkan's Scalar Block Layout. (Note that this comes with a performance warning).
  • All / most of the above

Capability computation UX

Design and implement what I've been calling "capability computation":

  1. Let the user specify what capabilities to allow. Exactly how they do it is open right now - e.g. attributes, or just a -C target-feature temp hack.
  2. Validate those capabilities are correct. Error on missing capability, warning on not-needed capability?

Note validation is surprisingly complex, e.g. some capabilities say "you can't opbitcast this particular type to that particular type", it's not just simple "this instruction is/isn't allowed" rules.

@Jasper-Bekkers is working on part 2 right now.

Fix pointers in aggs needing SROA

this makes me want to cry ๐Ÿ˜ฟ

// build-pass

use spirv_std as _;

use core::panic::PanicInfo;
use glam::Vec4;

struct S<'a> {
    x: &'a Vec4,
    y: u32,
}

#[spirv(fragment)]
pub fn main(input: Vec4, mut output: &mut Vec4) {
    let s = S { x: &input, y: 5 };
    *output = *s.x;
}

Error

error: invalid id:0:0 - OpLoad Pointer <id> '26[%26]' is not a logical pointer.
  %27 = OpLoad %v4float %26

  |
  = note: spirv-val failed
  = note: module `$TEST_BUILD_DIR/hello_world.stage-id.spv`

SPIR-V wishlist

Tracking issue for features we think are missing or useful in to have in SPIR-V

  • OpTrap: trigger a breakpoint

Error running Example on macOS

When I tried to the run the cargo run --bin example-runner example I got this error. Running on macOS 10.15.6 with rustc 1.49.0-nightly (8dae8cdcc 2020-10-12).

   Compiling example-runner v0.1.0 (/Users/src/rust-gpu/examples/example-runner)
    Finished dev [unoptimized + debuginfo] target(s) in 5.64s
     Running `target/debug/example-runner`
thread 'main' panicked at 'called `Result::unwrap()` on an `Err` value: LoadingError(DlOpen { desc: "dlopen(libvulkan.dylib, 2): image not found" })', examples/example-runner/src/main.rs:232:38
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

GPU-side layout/binding attributes

Higher-level related issue: #10

Right now, there's not really a way to specify binding location, etc. - we need to figure that out. Relatedly, either figure out a way to use statics to declare all the bind-y stuff, or decide we're going with parameters.

This could be a temp hack/workaround until we figure out more what the final design should look like, in the linked higher-level issue.

Define proof of concept gool & phase 1 of development

@khyperia @Jasper-Bekkers and I discussed in our first weekly meeting today about what should be the first phase of development of this and what is the goal with our proof of concept.

We said something like this:

  • Get the initial basic Rust to SPIRV new compiler prototype up. This is what @khyperia has started on (let's file an issue on it)
  • Convert a few of our own shaders to Rust #4
  • Plug in the Rust compiled SPIRV shaders into our engine and verify it actually works
  • Flesh out design documents covering the sub projects, design space and issues so we know the full scope of the project and what would be needed

Outcome: Either we are happy with the progress & direction and we make the project open source and continue into a next phase of full development to make this real and that we actively use, or we decide to shut down / push the project

Timeframe: 1-2 months

Does that sound about right @Jasper-Bekkers @khyperia, anything missing or that should phrase differently?

Do we use spirv-val?

(for end users, for development we're definitely going to use it) - this came up in #31

Say, for example, that the user does something invalid under the current capabilities - e.g. use OpBitcast on a pointer without the Addresses capability, or a storage class that's unsupported. We have two ways of detecting this error:

  1. Detect this when we're compiling, and give an error immediately.
  2. Run spirv-val on the result binary, and report any errors as rust errors.

There's a trade-off here: 1 takes way more work, and 2 produces lower quality errors. So, opening up a design issue to talk about it.


Of course, the answer might be, as always, a mix of the two. For example, we could try to detect all errors while we're compiling, but realize that there will be holes, and still run spirv-val on the result - and when spirv-val detects something that we don't, add in a check for that in the compiler.

Open up and announce project

Tracking and discussion issue for when to open this project to the public and announce it.

We've said we need at a minimum some real running "hello world" shader, but ideally some Ark shaders (#4 & #36), as well as have good guides and examples in the repo

Language feature: barriers

We want to have the same types of safety on the GPU as we do on the CPU. One of those areas that make it easy to introduce race conditions is with memory barriers. HLSL and GLSL require the programmer to explicitly put them in the right locations without any formal verifiction.

This is an attempt at solving that within the Rust type system (attempt still has racy bugs and is largely incomplete).

use core::ops::Deref;
use core::ops::DerefMut;
use core::ops::Index;
use core::ops::IndexMut;

struct MyFoo {
    test: GroupSharedArray<u32>,
}

struct GroupSharedArray<T>
where
    T: Default + Copy,
{
    data: Box<[T]>,
    size: usize,
}

impl<T: Default + Copy> GroupSharedArray<T> {
    fn new() -> Self {
        Self {
            size: 100,
            data: Box::new([T::default(); 100]),
        }
    }
}

struct GroupSharedWriter<T> {
    data: T,
}

impl<T> Deref for GroupSharedWriter<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl<T> DerefMut for GroupSharedWriter<T> {
    fn deref_mut(&mut self) -> &mut Self::Target {
        &mut self.data
    }
}

impl<T> GroupSharedWriter<T> {
    fn new(data: T) -> Self {
        Self { data }
    }
    fn barrier(self) -> GroupSharedReader<T> {
        GroupSharedReader { data: self.data }
    }
}

struct GroupSharedReader<T> {
    data: T,
}

impl<T> Deref for GroupSharedReader<T> {
    type Target = T;

    fn deref(&self) -> &Self::Target {
        &self.data
    }
}

impl<T> GroupSharedReader<T> {
    fn barrier(self) -> GroupSharedWriter<T> {
        GroupSharedWriter { data: self.data }
    }
}

impl<T: Default + Copy> Index<Uniform<u32>> for GroupSharedArray<T> {
    type Output = T;

    fn index(&self, index: Uniform<u32>) -> &Self::Output {
        &self.data[index.data as usize]
    }
}

impl<T: Default + Copy> IndexMut<Uniform<u32>> for GroupSharedArray<T> {
    fn index_mut(&mut self, index: Uniform<u32>) -> &mut Self::Output {
        &mut self.data[index.data as usize]
    }
}

struct Uniform<T> {
    data: T,
}

impl<T> Uniform<T> {
    fn new(data: T) -> Self {
        Self { data }
    }
}

fn thread_idx() -> Uniform<u32> {
    Uniform::new(0)
}

fn main() {
    let mut data = GroupSharedWriter::new(MyFoo {
        test: GroupSharedArray::new(),
    }); // new does barrier

    data.test[thread_idx()] = thread_idx().data;
    data.test[thread_idx()] = 666; // should be illegal

    let value = data.test[thread_idx()]; // should be illegal

    //data.test[0] = thread_idx().data; // what happens in this case? it's unsafe/unsound. race cnd over contents of `test`

    let other_data = data.barrier(); // `barrier` return GroupSharedReader
    
    // some race-y cases handled correctly:
    // data.test[thread_idx()] = 666; // is correctly marked illegal
    // other_data.test[thread_idx()] = 666; // is correctly marked illegal

    let test = other_data.data.test[thread_idx()];
}

// tl;dr
// - use move semantics to enforce barriers betwen reading and writing
// - broken right now because DerefMut and IndexMut require Deref and Index to be implemented
// - broken right now because we can write to the same location multiple times (data race)

Add example using gfx-hal or wgpu

In addition to the existing example using ash, which is quite explicit and low-level with Vulkan, it would also be very nice to have an example using gfx-hal which would be cross-platform and likely smaller and cleaner example.

@termhn has volunteered :)

Investigate adding fast-math functions

Relevant: rust-lang/rust#21690

My current thinking around this is that we probably shouldn't even do this.

However, if we do this, I'm kind of partial to having something like unsafe fn sqrt_approx() implemented on floats, that one needs to call explicitly within an unsafe block. As discussed in that issue, there are quite a few problems with the existing LLVM backend, it's optimizations etc. Many of which we don't need to care about because we're rolling our own. Having said that, SPIR-V and especially Vulkan have their own set of restrictions around floats: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/html/vkspec.html#spirvenv-precision-operation

Meeting 2020-08-28

Previous meeting: #15

Rough agenda: TBD (please add!)

  • General status update - @khyperia
  • @VZout is getting up to speed on project, are there specific areas he can contribute to to accelerate the prototype? - @repi
  • Discuss potential consultants, internships & master thesis students contributing (possible? who, how, when) - @repi
  • Discuss bindings proposal #19

Add bindings for spirv-opt

We will be relying on spirv-opt for actually making the generated spirv code go brrrrr, but right now there are no rust bindings for it, so we can't call it from Rust like we need to.

There are a couple of complications to this.

  1. The code is all C++, and there's no C API
  2. CMake, because of course

For the first point, the actual API for running the optimizer seems to be fairly trivial, so I see 2 paths forward for this.

  1. Write a small C wrapper around the C++ API and write manual bindings for it
  2. Try out cxx though my guess right now is that it is overkill

As for CMake, just as with our PhysX bindings, we will obviously just build it with cc so that downstream users won't hate us.

I'm putting this issue here for now, but I am thinking that this would probably be its own repo which contains the bindings/wrapper crates in it, and it would initially just be for the spirv-opt part of spirv-tools, but obviously could add the other tools as needed.

Meeting 2020-09-17

Previous meeting: #26

Potential agenda (please add/edit in this post)

  • Prototype compiler status update - @khyperia
  • Welcome @termhn and @eddyb to the project, how to work async & what are first potential tasks and areas? - @arirawr & @repi
  • Figure out meeting time going forwards (this meeting is a one-off temp time shift - is this time good, too early for US folks, etc.?)
  • Ark Q4 objective of bringing up all/most Ark shaders in Rust, how feasible and what is needed? - @repi

Scalarizer-checker

Much like the borrow checker checks lifetimes, I think it might be important for a modern, safe GPU language to be able to check and verify access to scalar registers and to validate scalar control flow.

Right now the concept of scalars isn't exposed in SPIR-V as a first class citizen (and it might never be), however experiments in ISPC and discussions with IHVs have show that this is indeed an issue. Doing this properly can mean both a performance win (more scalarized code is typically better) and a safety win (e.g. preventing writing to vector registers from scalar branches would be a simple example).

https://ispc.github.io/ispc.html#uniform-and-varying-qualifiers
https://ispc.github.io/ispc.html#uniform-control-flow

To be clear, this isn't about the distinction from float and Vec4, this is about sgpr vs vgrp.

Examples include GCN/RDNA's s_ namespaced registers and instructions (as opposed to the v ones https://developer.amd.com/wp-content/resources/RDNA_Shader_ISA.pdf). And NVIDIA's URX vs RX registers in Turing: https://docs.nvidia.com/cuda/cuda-binary-utilities/index.html

Meeting 2020-10-08

Previous meeting: #39

Meeting agenda: Discuss project scope overall and go/nogo for next phase(s)

(add links to issues and material for everyone to readup on beforehand)

  • Overview of reasons for the project to exist, and "levels"/milestones/roadmap: #47. Good to read beforehand, it's a lot.
  • After go/nogo, reaffirm our requirements before open-sourcing, and anything the community team can start on now to prepare for that. (@arirawr)

Add Issue Templates

GitHub allows you to provide templates for issues. I think it would be good to have some basic ones start us off with, and we can add or remove as appropriate.

Potential Templates

  • Bug Report
    • Similar Rust's ICE template, including relevant code, compiler version, platform, (and GPU?).
  • Feature Requests

Roadmap, milestones, and functionality levels

High-level reasons for the project

Pros compared to glsl/hlsl/etc.:

  • Rust is a nicer language: memory saftey, traits/generics, and so on.
  • Whole codebase in one language is good.
  • IDE support (syntax highlighting! autocomplete! omg!)
  • Cargo and crates.io (see also: ideas around shadergraph-like things)
  • Debugging on CPU? (from println to true debuggers)

Cons:

  • glsl has huge ecosystem with an insane amount of resources being pumped into the status quo, hard to beat that
  • glsl is a specialized language, having a general purpose language be restricted down to GPU-allowed stuff is awkward.

Functionality levels

Each level distinguishes a milestone of achievement, and requires a certain level of commitment to get there. However, items here are extremely fluid, frequently span multiple levels, and can be worked on non-sequentially and in parallel.

Level 1 (#5), "prototype":

  • The compiler outputs a functional spir-v binary that can be executed, even if doing so is complex, user-unfriendly, and high-effort.
    • There's an implicit "compiler is better and supports more stuff" work item for every level past this.
  • Headcount commitment: At least one dedicated compiler engineer, a few support engineers. Code is suspiciously already in.
  • Time commitment: A tad over 2 months. Exactly. Wau, that's an accurate "estimate"!

Level 2 (#36), "that's a rusty skybox":

  • Ark is using rust-gpu shaders in "production". The system may be arkane (:3) and hacked together, but it's functional.
  • Headcount commitment: At least one dedicated compiler engineer, a few support engineers. Same as level 1.
  • Time commitment: 0.5-1 month. Likely to be blurred with level 3.

Level 3, "it's friendly now":

  • Build system integration is functional and user-friendly - e.g. someone can specify a crate path in build.rs, and include! the resulting spir-v binary. (#48)
  • The spirv-std library has the basics. (e.g. #8, #11, #12, #34, #45).
    • Implicit "stdlib is even better" item for every level past this.
  • The compiler is reasonably friendly (#42, #43, #44).
    • Again, implicit "compiler is even friendlier" item for every level past this.
  • Consider open-sourcing here, as it will be usable externally (even if a little difficult to do so). (#37)
  • Headcount commitment: Preferably multiple compiler folks, at least one build system engineer, community manager for open-sourcing and guiding that community, some partial-time folks for various tasks (stdlib work) and feedback (both actual part-time and folks in other pods dipping in from time to time)
  • Time commitment: 3 months (?)

Level 4, "time to submit a talk about this":

  • Ergonomic bindings, i.e. generating host-side code (#10)
    • Note this begins to be framework-dependent, having a hard dep on e.g. ash or vulkano specifically. We need to be careful here to make sure the underlying compiler is still usable elsewhere.
  • Better compiler support for IDEs (having the gpu restrictions show up in rust-analyzer)
  • This is where using rust on the gpu instead of glsl starts to flicker as a candle of hope.
  • Headcount commitment: Uncertain, likely similar to level 3.
  • Time commitment: 2 months (?)

Level 5, "I could see this in a released game":

  • Rendergraph shenans (#21)
  • Upstream the compiler
  • Clippy rules
  • Rust on the GPU starts to really shine as a useful tool.
  • Headcount commitment: Uncertain. Depends on what development style we want: could dump a bunch of people on it (a bit more than level 3) and get it beautiful quick, or have a couple folks slowly burning on it for a while.
  • Time commitment: 4-6 months (??) - (Ashley has no knowledge of how long rendergraph shenans would take to implement)

Level 6, "all the cool kids use shaded rust":

  • Move through rust support tiers until we hit tier 1
  • Begin to revolutionize shader development (hopefully~), really hitting all the use cases where Rust is great at this.
  • Headcount commitment: Uncertain, likely similar to level 5.
  • Time commitment: unbounded, uncertain, will definitely have more roadmap discussions before we get here

Feel free to edit this issue with more thoughts or to shuffle things around.

Name of project?

We haven't aligned on really what to call this yet, it is essentially just "Rust on/for GPU" or "Rust GPU" in short, which is pretty decent and does differentiate from @MaikKlein 's previous RLSL prototype ("Rust Like Shading Language") for now.

Don't have to decide now though but thought we can have a discussion issue for it.

Convert a few GLSL shaders to Rust

We talked about today that we should convert a few of our existing rendering shaders to Rust to see how well we could express it, even if we can't compile it to SPIRV or run them yet. Having a few GLSL or HLSL shaders here in the repo that we also have a manually converted Rust versions of, initially maybe just rough Rust psuedo code but can also try and build them in a Rust CPU crate project to actually compile it.

Can take for example the Ark mesh and sky shaders, they are quite basic and fine to duplicate to this repo for testing.

This would be a helpful guide to see what functions we would want in a shader core crate, that doesn't exist in Rust std, also for easy porting of existing shaders. And will be a guide to see what areas of Rust are the least ergonomic for shaders where we may want to have Rust language shaders to simplify it.

Meeting 2020-10-15

Previous meeting: #46

Time: 17:00 CEST
Where: #rust-gpu voice channel on Ark Discord.

Agenda

(please edit/add)

  • Classic @khyperia overview
  • @eddyb joining meeting, yay!
  • @XAMPPRocky joining - discuss potential engagement with the project (@arirawr)
  • Discuss steps towards opening project now soon, how does it feel and what more do we need? #55
    • Let's anticipate some FAQ so @arirawr and others can have responses, incorporate into project documentation as well
  • Follow up on people & resourcing for the project

Improve Error Message When Missing SPIR-V tools

Currently if you're missing the SPIR-V tools it gives you a No such file error along with an ICE. Both of these error messages don't really tell you how to solve the issue, and the ICE is a bit misleading since it's not a bug in Rust. It would be nice if it could exit gracefully with a message asking to install SPIR-V tools.

  thread 'rustc' panicked at 'spirv-val failed to execute: Os { code: 2, kind: NotFound, message: "No such file or directory" }', rustc_codegen_spirv/src/link.rs:158:25
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

  error: internal compiler error: unexpected panic

  note: the compiler unexpectedly panicked. this is a bug.

  note: we would appreciate a bug report: https://github.com/rust-lang/rust/issues/new?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md

  note: rustc 1.49.0-nightly (8dae8cdcc 2020-10-12) running on x86_64-apple-darwin

  note: compiler flags: -Z unstable-options -Z codegen-backend=librustc_codegen_spirv.dylib -C opt-level=3 -C embed-bitcode=no --crate-type dylib

  note: some of the compiler flags provided by cargo are hidden

  error: could not compile `example-shader`

  To learn more, run the command again with --verbose.
  Error: BuildFailed

Render graph design

A high-level goal for this project is to also design a GPU rendering/compute graph to tie it all together and enable users to to develop small reusable graph components inside crates that contain Rust CPU and GPU code and that can be easily connected and tweaked.

We talked briefly about it today in #15, but will require quite a bit of design and the lower level parts of SPIRV codegeneration and Rust bindings overall (#10) is higher priority

Emoji for project?

All Embark open source projects are required to have a unique emoji representation, that is the law.

So we need to pick one before opening up this repo & project!

Previous examples:

Related to #2 also as if we go with a different name than "Rust GPU" the emoji have to fit with that

List previous Rust on GPU initatives and related projects

We talked in the meeting today (#15) that it would be good to list out all previous related Rust GPU initiatives and projects in the README, and maybe a bit how they relate or differ from the approach(es) we are taking here.

Could put them under a "Previous Work and Related Projects" section

  • 2013 - Experimental Rust PTX compute backend on Nvidia GPU
    • @pcwalton do you have any more links or info about it?
  • 2018 - Accel
  • 2019 - RLSL by @MaikKlein. This we do mention in the README, but can compare more

Likely more?

Convert panics to compiler errors

There's a whole heck of a lot of panics, unwraps, and what-have-yous in rustc_codegen_spirv right now. Nearly all of them should probably be compiler errors instead.

Note this may require some rustc PRs, since right now, backends don't have access to span information. @eddyb may have some ideas around how to get span information, e.g. a bx.set_span method.

Open source repo & project

Tracking issue for what we need to do before opening this project now soon! Let's link relevant issues and have checkboxes for them here and drive discussions about it here

What to do before:

  • Some real running "hello world" shader
  • Go through issues and docs and make sure we don't have anything in it we DON'T want to share
  • Good enough initial onboarding experience #28
  • Include small example Rust shader and screenshot in top of README
  • @repi may have thoughts about #48, it'd be nice to have those before opensourcing (and so vaguely public API)
  • Draft an announcement post to GH releases.
  • Clean up the README: move background/"why embark" sections to post? That would help get the "scope" section more visible, which seems to be a priority.
  • Go through README and tweak some language (mention @MaikKlein and RLSL prototype connection more also?) @repi

CUT for v0.1 release (may do later):

  • Ideally some Ark shaders #36 (we have a simple similar sky shader instead)
  • Sketch out "Compiler Engineer" job ad would be excellent to have posted when we open source the project

Release checklist:

  • Open repo
  • Open discord
  • Un-draft the release post
  • Announce in Discord, link release post
  • @repi tweet it out, link release post
  • Add project to embark.dev and embark.rs - @arirawr
  • Post on users.rust-lang.org and r/rust - who?

Once released:

Meeting 2020-08-20

Previous meeting: #6

Rough agenda

  • Give technical overview of project to @pcwalton - @khyperia
  • Potential questions/discussion topics with @pcwalton
    • What's the probability of getting language features in? e.g. designs for pointer address spaces (anywhere from tweaking things to make an intrinsic wrapper struct ergonomic, to actual syntax in e.g. ref type decls) - @khyperia
    • Areas to collaborate on and how?
  • Discuss first sketches on converted Rust shaders #14
  • Light RFC process (#18) and/or markdown design docs?

Add spirv-tools to github actions to enable CI testing

tl;dr uncomment this and get it to work

# TODO: We need to install https://github.com/KhronosGroup/SPIRV-Tools on the system to be able to run `cargo test`
# check:
# name: Test
# strategy:
# matrix:
# os: [ubuntu-latest, windows-latest]
# runs-on: ${{ matrix.os }}
# steps:
# - uses: actions/checkout@v2
# - uses: actions/cache@v2
# with:
# path: |
# ~/.cargo/registry
# ~/.cargo/git
# target
# key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
# restore-keys: |
# ${{ runner.os }}-cargo-
# ${{ runner.os }}-
# - uses: actions-rs/toolchain@v1
# with:
# toolchain: nightly
# components: rust-src, rustc-dev, llvm-tools-preview
# - uses: actions-rs/cargo@v1
# with:
# command: test

In CI, we'd like to be able to run cargo test. However, that requires spirv-tools to be installed on the system - e.g. we want to test if the modules we're generating are actually valid via spirv-val. This might be a good first issue if you already know how github actions work, and how to get spirv-tools bundled into a package installable on the CI system.

If nobody picks this up in a week or so, I'll try to figure it out myself (and probably fail a lot in the process but hey I'll try)

"Build system"

This is a tracking issue for what I've been calling the "build system", but in reality the full task list is larger than just that.

In general, this is a group of related issues that would be great for someone other than me to own and drive forwards. These tasks are spread across various "functionality levels", see that issue for more information.

  • Create a crate to be used by users build.rs, that takes a path to a shader crate and spits back a spir-v binary that can be include!d (or perhaps a better designed version of this). I've created an extremely rough and minimally-featured prototype in the spirv-builder crate - we need to expand it quite a bit, in both robustness and features.
  • Integrate spirv-opt and spirv-val into statically linkable libraries that are included in rustc_codegen_spirv. This means the user will not have to install spirv-tools on their system. Tracking issue: #31
  • Generate host-side bindings, tracking issue: #10. I like the approach of "SYCL compiles down it's GPU side C++ code along with some side-cart metadata first, this metadata is then converted into regular host side C++". This means that rustc_codegen_spirv would spit out some form of metadata for what bindings are in the module, and then the build.rs helper crate would generate rust source files from that metadata. (Or perhaps rustc_codegen_spirv would spit out rust files directly and the builder would just include them, but, whatever, exact behavior to be determined later. Having multiple backends - e.g. ash and vulkano both - complicates this.)
  • Possibly adding any hooks/etc. to that generated code to enable the rendergraph/flamegraph support.

Minimal repro passing struct to a function

#![no_std]
#![feature(register_attr)]
#![register_attr(spirv)]
#![feature(core_intrinsics)]

use core::panic::PanicInfo;
use spirv_std::{f32x4, Input, Output};

#[repr(C)]
pub struct Vec3(pub(crate) f32, pub(crate) f32, pub(crate) f32);

impl Vec3 {
    #[inline]
    pub fn new(x: f32, y: f32, z: f32) -> Self {
        Self(x, y, z)
    }

    #[inline]
    pub fn splat(v: f32) -> Self {
        Self(v, v, v)
    }
}

fn sky(_a: Vec3, _b: Vec3) -> Vec3 {
    Vec3::splat(1.0)
}

#[allow(unused_attributes)]
#[spirv(entry = "fragment")]
pub fn main(input: Input<f32x4>, mut output: Output<f32x4>) {
    let color = input.load();

    let k = sky(Vec3::new(0.0, 1.0, 0.0), Vec3::new(0.0, -1.0, 0.0));

    output.store(f32x4(color.0 + k.0, k.1, k.2, 0.0))
}

#[panic_handler]
fn panic(_: &PanicInfo) -> ! {
    loop {}
}
C:\Users\Jasper\embark\rust-gpu>cargo build
   Compiling example-runner v0.1.0 (C:\Users\Jasper\embark\rust-gpu\examples\example-runner)
error: failed to run custom build command for `example-runner v0.1.0 (C:\Users\Jasper\embark\rust-gpu\examples\example-runner)`

Caused by:
  process didn't exit successfully: `C:\Users\Jasper\embark\rust-gpu\target\debug\build\example-runner-a7fe7e8688934126\build-script-build` (exit code: 1)
  --- stderr
     Compiling example-shader v0.1.0 (C:\Users\Jasper\embark\rust-gpu\examples\example-shader)
  error: spirv-val failed with exit code: 1
    |
    = note: module "C:\\Users\\Jasper\\embark\\rust-gpu\\examples\\example-shader\\target\\spirv-unknown-unknown\\release\\deps\\example_shader.spv"
    = note: error: line

 55: OpVariable Initializer <id> '20[%20]' is not a constant or module-scope variable.
              %32 = OpVariable %_ptr_Function_Vec3 Function %20




  error: aborting due to previous error

  error: could not compile `example-shader`

  To learn more, run the command again with --verbose.
  Error: BuildFailed

example_shader.txt

SPIRV-Tools Relooper Implementation (SCF)

For the first prototype implementing the Relooper algorithm seems like a good option. It is wel documented and multiple implementations are open sourced which can be used as reference. While it is not as efficient as other solutions it's a start.

As suggested by David Neto, I'm building a seperate library in the SPIRV-Tools project that depends on spirv-opt. I've split up the task in 2 parts: Structurizing the control flow and inserting the SPIR-V specific ops (OpLoopMerge, OpSelectionMerge , etc).

Structurizing SPIR-V Using Relooper.

  • Generate Shapes
  • Emit SPIR-v
    • Insert labels
    • Branch (implemented but I'm super doubtful of the implementation)
    • Block
    • Simple (shape)
    • Loop (shape)
    • Multiple (shape)

Inserting SPIR-V Control Flow Ops

  • OpLoopMerge
  • OpSelectionMerge
    ...

Good project onboarding experience

We discussed in the team meeting today (#26) that there would be a lot of value in making sure we have a great onboarding experiencing in this project and with new contributors that want to contribute to this once the project is public.

@MaikKlein have mentioned before that this was one of the challenges with the RLSL prototype that it was quite complex to set up and he needed to answer a lot of messages and support people that just wanted to try it out.

Let's try to make that as easy as possible with clear README, documentation, setup guides, examples, etc. Think @arirawr can help here once we are close to opening up. And would be great for every new dev that joins this project to improve the experience and comment in here what to change/fix that would be helpful to them.

@maxded also mentioned that it would be great to just have good references included to books and articles about how to learn more about building compilers, codegen etc as well.

Structured control flow

MIR internally is pretty much completely unstructured which leads to some problems since in the flavour of SPIR-V that we're trying to support the control flow needs to be fully structured. The only flavor that supports unstructured controlflow is for OpenCL.

This requires potentially quite a bit of engineering to solve since the algorithms involved are relatively complex.

One avenue of exploration could be to see if we can shoehorn the Mesa3d controlflow restructurizer back into our compile chain:

Another option would be to implement either Stackifier or Relooper as part of spirv-opt that we run after the fact. We may already need to run spirv-opt to run mem2reg since we'll mostly be emitting OpLoad and OpStore instead of sticking to SSA proper.

Another option is one that @MaikKlein proposed:

There is I think another option that I haven't explored yet, and that is to remove some mir passes. Basically I think if you would remove most of the mir passes between HIR and MIR then the resulting cfg should be fairly easy to work with. I think most of the information will be lost during some of the optimization passes. Recomputing the control flow is fairly easy.

This option would leave the high-level controlflow constructs in tact without ever lowering them to branches/jumps which is a lot more robust then trying to reconstruct the structured controlflow from the jumps.

Some other projects have also run into similar issues:

  • SPIRV-LLVM-Translator (translates LLVM to OpenCL style SPIRV) KhronosGroup/SPIRV-LLVM-Translator#30 (comment)
  • SPIRV-Cross has an outstanding issue about this: KhronosGroup/SPIRV-Cross#889
  • @MaikKlein ran into this issue with RLSL as well MaikKlein/rlsl#8
  • DirectXShaderCompiler's SPIR-V backend does work properly in this case, they've decided to emit SPIR-V more or less straight from the AST, which sits at a higher level then what we're currently doing (and are comfortable with). It's unlikely that the rustc team would accept such a change back into their mainline, or that they would support not breaking such a codepath.

Address space struct design

Issue to discuss how to handle pointer address spaces.

spir-v has the concept of Storage Classes which are declared both on pointer types, as well as load/store instructions. We need to somehow carry through the correct pointer type and emit the correct metadata:

Some thoughts off the top of my head:

Conservative option: declare some intrinsic structs:

struct WorkgroupMemory<T> {
    #[spirv(workgroup_memory)]
    ptr: *mut T,
}
impl<T> WorkgroupMemory<T> {
    pub fn load(&self) -> T {
        spirv_intrinsic_load_workgroup_memory(self.ptr)
    }
}

Way-too-aggressive option: add syntax to Rust:

fn f(x: &workgroup u32);

Vector types

spir-v has vector types that are dramatically useful on e.g. nvidia hardware for optimizing loads/stores. At first, let's just directly expose vector types, instead of trying to do things like auto-vectorization.

  1. Rustc already has the notion of vector types, and we directly translate them to spir-v vector types. We need to investigate under what conditions rustc emits vector types, and if we can re-use the same framework.
    Abi::Vector { ref element, count } => {
    let elem_spirv = trans_scalar(cx, ty, element, None, is_immediate);
    SpirvType::Vector {
    element: elem_spirv,
    count: count as u32,
    }
    .def(cx)
    }
  2. If we cannot re-use the same framework, create an attribute to specify a type is a builtin vector, recognize the attribute in abi.rs (example for storage classes), and emit a vector type. Perhaps do a mix of the two, e.g. if rust supports f32x2 and f32x4, but not f32x3, then we need to define our own f32x3.
  3. Investigate the spec: is emitting a spir-v "OpTypeVector" enough, or are there additional decorations/etc. we need to apply to get it to be a proper "vector type"?
    SpirvType::Vector { element, count } => cx.emit_global().type_vector(element, count),
  4. Implement the above in spirv-std, adding things like swizzling ops and other shenanigans (can come later).

This seems like a good not-quite-first-but-kinda-first task for someone other than me to do. Step 2 is a little involved and will likely need some mentorship - abi.rs is hecking wild - but getting more people who know how abi.rs works would be really really good.

Linker

There's a set of operations post-compilation that we need to do, that I'm lumping together and calling "linking":

  1. On a most basic level, libcore and user code will be compiled separately, into two spir-v modules, that will need to be combined.
    1.1) On a more realistic level, rustc "codegen units" compile crates into possibly more than a dozen modules, plus linking in external crates.
  2. Dead code elimination
  3. Capability computation - auto-detect what capabilities the module is using and add those to the module, check to make sure the actual capabilities are what the user expected. (very important to do this after DCE)
  4. Other potential weird processing stuff we need to hack together (for example, for a bit there might have needed to be a pass to clean up OpTypeForwardPointers, but turns out that's not needed)

There's two options that we could do:

  1. Re-use the spirv-tools linker, in c++, then implement DCE etc. in another pass after the linker
  2. Write our own linker, and link and DCE etc. at the same time with the same code

2 seems to be much more reasonable, because it seems like we're going to have to implement a lot of that complexity anyway, and having exact control over the quirks seems extremely useful - and our output is certainly going to be quirky.

@Jasper-Bekkers is currently working on this.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.