Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Find a solution for shader language support #71

Open
cmr opened this issue Jul 5, 2014 · 29 comments

Comments

@cmr
Copy link
Contributor

commented Jul 5, 2014

Different graphics APIs have their own shading languages or ideas for what a shading language should be. In our quest to support multiple backend APIs, we need a way to unify these.

@cmr cmr added the question label Jul 5, 2014

@cmr

This comment has been minimized.

Copy link
Contributor Author

commented Jul 5, 2014

One idea is to just query for the backend in use and have the user themselves supply a shader per API.

@cmr

This comment has been minimized.

Copy link
Contributor Author

commented Jul 5, 2014

Another is to use a single shader language and translate to whatever the backend needs. HLSL might be a mature choice here. (Valve has a tool available but I'm not sure how quality it is, and it only supports Shader Model 3.)

@cmr

This comment has been minimized.

Copy link
Contributor Author

commented Jul 5, 2014

Another is to make our own shading language, but that just sounds like a bad idea.

@brendanzab

This comment has been minimized.

Copy link
Member

commented Jul 5, 2014

A HM typed concatenative language!

jk.

@Kimundi

This comment has been minimized.

Copy link
Contributor

commented Jul 5, 2014

I'm currently toying around with a "umbrella" struct for providing the source of the same logical shader in different languages. Nothing fancy, something like this:

pub enum DeviceShader {
    StaticBytes(&'static [u8]),
    OwnedBytes(Vec<u8>),
    NotProvided
}

pub struct ShaderSource {
    pub glsl_120: DeviceShader,
    pub glsl_150: DeviceShader,
    hlsl_sm_3: DeviceShader
    ...
}
...
static VERTEX_SRC: ShaderSource = shaders! {
GLSL_120: b"
    #version 120
    attribute vec2 a_Pos;
    varying vec4 v_Color;
    void main() {
        v_Color = vec4(a_Pos+0.5, 1.0, 1.0);
        gl_Position = vec4(a_Pos, 0.0, 1.0);
    }
"
GLSL_150: b"
    #version 150 core
    in vec2 a_Pos;
    out vec4 v_Color;
    void main() {
        v_Color = vec4(a_Pos+0.5, 0.0, 1.0);
        gl_Position = vec4(a_Pos, 0.0, 1.0);
    }
"
};

That should at least make it possible to hack some legacy compatibility into the libs without giving up on the newer shader versions at all.

@brendanzab

This comment has been minimized.

Copy link
Member

commented Jul 6, 2014

#72 gets us some of the way there.

@kvark

This comment has been minimized.

Copy link
Member

commented Jul 7, 2014

Found a good link for that: Cross Platform Shaders in 2014

@kvark kvark added T-discussion and removed T-question labels Jul 16, 2014

@cmr

This comment has been minimized.

Copy link
Contributor Author

commented Aug 22, 2014

I think what we have today is as good as we are going to get. Anything else can be built on top of it, externally, and possibly merged back in.

@cmr cmr closed this Aug 22, 2014

@cmr cmr added the newcolumn1 label Aug 22, 2014

@kvark

This comment has been minimized.

Copy link
Member

commented Feb 2, 2015

Shaders become one of the major usability bottleneck of gfx-rs. Let's continue this discussion, even if the code for it will end up in a separate library.

@kvark kvark reopened this Feb 2, 2015

@kvark

This comment has been minimized.

Copy link
Member

commented Feb 2, 2015

This might be worth looking at: https://github.com/kmcallister/glassful
Also, cc @csherratt, @kmcallister

@kvark kvark referenced this issue Feb 2, 2015

Closed

Lessons from Glium ergonomics #515

5 of 5 tasks complete
@brendanzab

This comment has been minimized.

Copy link
Member

commented Feb 3, 2015

Something to also think about is whether we want to support recompiling of shaders at runtime.

@kmcallister

This comment has been minimized.

Copy link

commented Feb 3, 2015

I would very much like that feature.

@kvark

This comment has been minimized.

Copy link
Member

commented Feb 7, 2015

Inspired by Glassful and @csherratt's example code from Reddit, I tried to develop it further. Here is an example of what I'd like to use (very roughly):

/// uniforms
struct MyParams {
    color: vec4,
}
/// vertex buffer 1
struct MyBuf1 {
    pos: vec2,
}
/// vertex buffer 2
struct MyBuf2 {
    tc: vec2,
}
let technique = glassful! {
    struct Varying {
        tc: vec2,
    }
    // @Position is a special vertex output, not sure if we can make it more sound
    fn vertex(_params: MyParams, buf1: MyBuf1, buf2: MyBuf2) -> (@Position, Varying) {
        (vec4(buf1.pos, 0.0, 1.0), Varying { tc: buf2.tc })
    }
    fn fragment(params: MyParams, _var: Varying) -> vec4 {
        params.color
    }
    static pass0 = pass(vertex, fragment);
};
...
let program = device.link_program(&technique.pass0).unwrap();

The implementation doesn't seem trivial. Here are the biggest problems that I see:

  1. How to bring some struct definitions (i.e. MyParams, MyBuf1) into the macro, or outside of it?
  2. How to extract only the relevant items from the whole program when compiling a particular stage? I.e. a fragment program should not contain definitions of vertex buffers.
  3. How to translate from Rust functional style into GLSL/whatever imperative one? Just printing out the text while traversing the AST doesn't seem enough.
  4. How to support the run-time compilation, where the shader parameter struct and vertex formats are not known in advance in compile time?

Any ideas, feedback, suggestions - please let me know.

@ghost

This comment has been minimized.

Copy link

commented Feb 8, 2015

I have been playing around with this a bit in rusterize.

I'm not sure we even need @position, we could just require the vertex sharer to fill slot 0 of the tuple with the position. If it is not a vec4 the macro can reject it.

    // Placing a variable in a Flat<T> makes the compiler place a flat property around it for the fragment shader
    fn vertex(_params: MyParams, buf1: MyBuf1, buf2: MyBuf2) -> (vec4, Flat<vec4>) {
        (vec4(buf1.pos, 0.0, 1.0), buf2)
    }
    fn fragment(params: MyParams, (position, color): (vec4, vec4)) -> vec4 {
        color
    }

This can be a bit hairy of you add to many varying:

    // Placing a variable in a Flat<T> makes the compiler place a flat property around it for the fragment shader
    fn vertex(_params: MyParams, buf1: MyBuf1, buf2: MyBuf2) -> (vec4, vec3, vec2) {
        (vec4(buf1.pos, 0.0, 1.0), buf1.normal, buf1.texture)
    }
    fn fragment(params: MyParams, (position, normal, color): (vec4, vec3, vec2)) -> vec4 {
        color
    }

The strategy I have for rusterizer is that a user can apply a trait to the output struct as a way to instruction the compiler how to get the position from the vertex shader's output. An addition trait is required to be implemented that will do the varying calculations between the triangles 3 points.

I don't image traits are going to be a nice way to actually implement this for glassful.

@kvark

This comment has been minimized.

Copy link
Member

commented Feb 9, 2015

@csherratt so your vertex output position is implicit. We can't extend this approach to other special inputs/outputs, because many of them are optional (like. gl_Position as fragment input, or gl_Depth as fragment output). That's why I believe we should have pre-defined types for all special things. Maybe not @Position but something that can be easily recognized (VertexPosition?), both by the macro code and the user reading it. I do like the Flat<T> idea, it would play well with special input/output types and allows to customize variable properties without actually using language property items.

@ghost

This comment has been minimized.

Copy link

commented Feb 9, 2015

There are quite a few you are right: https://www.opengl.org/wiki/Built-in_Variable_%28GLSL%29

The idea of using custom types for these works well in my mind. If you add one to the tuple, output struct we can easily find it. I guess for input it would not be much harder, but we do lose a bit of the input struct is the output struct.

@kvark

This comment has been minimized.

Copy link
Member

commented Mar 5, 2015

Something from g-truck about SPIR-V: http://www.g-truc.net/post-0714.html

@brendanzab

This comment has been minimized.

Copy link
Member

commented Mar 16, 2015

I so really want Vulcan/SPIR-V now!!!

@kvark

This comment has been minimized.

Copy link
Member

commented May 12, 2015

Note that SPIR-V specification is available, so while we are not able to test anything, we could start writing a translator.

@msiglreith

This comment has been minimized.

Copy link
Member

commented Aug 11, 2016

Current state of the SPIR-V ecosystem:

With the upcoming Vulkan backend a SPIR-V based shader compilation pipeline (e.g. GLSL -> SPIR-V -> backend(+ metadata)) might be interesting.

@LPGhatguy

This comment has been minimized.

Copy link
Contributor

commented Nov 6, 2016

bgfx has a solution using a C preprocessor to generate HLSL and GLSL (and thus SPIR-V) from a GLSL-like language -- it might be worth looking into.

See https://bkaradzic.github.io/bgfx/tools.html#shader-compiler-shaderc

@siriux

This comment has been minimized.

Copy link
Contributor

commented Mar 29, 2017

This initiative on 3d portability from the Khronos Group can be interesting:

https://www.khronos.org/3dportability

@siriux

This comment has been minimized.

Copy link
Contributor

commented Mar 29, 2017

I've found an SPIR-V 1.1 implementation in rust:

https://github.com/google/rspirv

This would allow to easily create some macros to write shaders in rust that generate SPIR-V, and from there to the final shader language.

For that last step, there is already an experimental tool:
https://github.com/KhronosGroup/SPIRV-Cross

Update: Include the cross tool

@msiglreith

This comment has been minimized.

Copy link
Member

commented Jul 20, 2017

Experience from inspirv-rust:

The goal of this project is/was to generate SPIR-V binaries from Rust via MIR.

What worked: example

  • Were able to create some basic SPIR-V shaders which empowered rendering of basic phong lighting teapot.
  • Crate and module support worked more or less out of the box, allowing to recrate shader specific core and std libraries
  • Attributes allowed to specify bindings and other meta stuff
  • Single file declaration for different shader stages

Issues:

  • MIR is nice as it's quite small and low level but to conversion to SPIR-V is very hard as SPIR-V is based on structured control flow SSA. This requires to recreate control flow information (not implemented!).
  • Keeping up-to-date with Rust can be difficult, MIR is still unstable and a lot of things changes
  • Using the compiler as library via accessing private compiled crates, which are shipped with the compiler, requires a lot of code duplication as a lot of useful functions are private
  • LLVM is deeply interwoven with the compiler, ripping it out is a major task (not addressed)

Future possibilities:
Going a step back and rather try to get it working without much hassle. Therefore, I will try our a HIR->raw shader text variant, which should be easier to implement as I don't need handle the structured control flow issue (but face some others, hopefully easier issues).
Advantage of this approach is also easier support different shading languages.

@eddyb

This comment has been minimized.

Copy link
Contributor

commented Sep 2, 2017

  • Using the compiler as library via accessing private compiled crates, which are shipped with the compiler, requires a lot of code duplication as a lot of useful functions are private
  • LLVM is deeply interwoven with the compiler, ripping it out is a major task (not addressed)

FWIW we want to fix these problems and ship a Cretonne backend within a year, hopefully.
What might be interesting is having "MIR -> Cretonne IR -> SPIR-V", i.e. using Cretonne as some sort of LIR for rustc.

It'd be nice to have more discussions and organization on these issues, but we (the Rust compiler team) have been spread thin for a while and it's showing.

@msiglreith

This comment has been minimized.

Copy link
Member

commented Sep 2, 2017

@eddyb Thanks for chiming in! Cretonne IR -> SPIR-V would probably be interesting, and also easier and cleaner compared to MIR->SPIR-V.
I'm still unsure how hard it would be to re-generate structured CFG from CIR (only have a limited knowledge in compiler construction!).

@eddyb

This comment has been minimized.

Copy link
Contributor

commented Sep 2, 2017

For structured CFG, the Relooper algorithm emscripten uses (used?) is probably a good starting point - not sure if SPIR-V is better than JS at this but at least it's still a CFG.

@blueridanus blueridanus referenced this issue Oct 28, 2017

Open

Make API fully backend-agnostic #1619

3 of 5 tasks complete
@kvark

This comment has been minimized.

Copy link
Member

commented Feb 12, 2019

triaged: still important to provide a nice shading language that would compile to SPIR-V, likely as a separate library.

@kvark

This comment has been minimized.

Copy link
Member

commented Jul 10, 2019

The ecosystem issue has been raised in rust-gamedev/wg#23

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
9 participants
You can’t perform that action at this time.