Giter Club home page Giter Club logo

rlsl's Introduction

Rlsl - Rust Like Shading Language

Deprecated in favor of rust-gpu

Join the chat at https://gitter.im/MaikKlein/rlsl

What is Rlsl?

Rlsl can compile a subset of Rust to SPIR-V. You can read more about the limitations here.

Rlsl targets the logical addressing model of SPIR-V.

The Logical addressing model means pointers are abstract, having no physical size or numeric value. In this mode, pointers can only be created from existing objects, and they cannot be stored into an object, unless additional capabilities, e.g., VariablePointers, are declared to add such functionality.

Features

  • Supports cargo
  • Multiple entry points can be defined in the same SPIR-V module
  • Currently supports Vertex, Fragment and Compute shaders
  • Shader code can run on the CPU because rlsl is a subset of Rust
  • Reflection TODO
  • Support library for interop between Rust and rlsl for uniforms (std140, std420) TODO

Installation

TODO

How?

RUSTC=rlsl cargo build

compile

How to install rlsl

Not ready to be used. Anything might happen

git clone https://github.com/MaikKlein/rlsl.git

Make sure you have both rustup and xargo installed.

cargo install-rlsl

Run tests

cd rlsl-test
cargo +rlsl test

Blog

  1. What is RLSL
  2. Milestone 1

Want to help?

Contribute

The project currently does not accept any contributions yet.

  • Rlsl can not be easily built by anyone
  • There is no documentation
  • Debugging tools are almost non existent
  • There is no infrastructure for testing
  • No guide level explanation for contributions

Rlsl will start to accept contributions after those issues are properly addressed.

Community

Want to chat? Join us on gitter.

Feel free to open an issue at any time.

rlsl's People

Contributors

gitter-badger avatar maikklein 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

rlsl's Issues

License?

Hi @MaikKlein, this project looks super exciting, please keep up the great work!

I'm currently working on a creative coding framework and would love to allow users to use something like RLSL in the future for graphics and compute. I'd like to contribute at some point, but would appreciate it if you could clarify your plans for the licensing of RLSL first. FWIW, I'd love something close to the license used by Rust itself, but this is of course just my opinion!

Handling alignment for uniforms and push constants

Requirements

Standard Uniform Buffer Layout

Every member of an OpTypeStruct with storage class of Uniform and a decoration of Block (uniform buffers) must be laid out according to the following rules:

  • The Offset decoration must be a multiple of its base alignment.

  • Any ArrayStride or MatrixStride decoration must be an integer multiple of the base alignment of the array or matrix from above.

  • The Offset decoration of a member must not place it between the end of a structure or an array and the next multiple of the base alignment of that structure or array.

  • The numeric order of Offset decorations need not follow member declaration order.

reference

These also then follow the rules from std140. Also mentioned in 14.5.4.

Additionally from the SPIR-V spec

Composite objects in the UniformConstant, Uniform, and PushConstant Storage Classes must be explicitly laid out. The following apply to all the aggregate and matrix types describing such an object, recursively through their nested types:

  • Each structure-type member must have an Offset Decoration.

  • Each array type must have an ArrayStride Decoration.

  • Each structure-type member that is a matrix or array-of-matrices must have be decorated with

    • a MatrixStride Decoration, and

    • one of the RowMajor or ColMajor Decorations.

    • The ArrayStride, MatrixStride, and Offset Decorations must be large enough to hold the size of the objects they affect (that is, specifying overlap is invalid). Each ArrayStride and MatrixStride must be greater than zero, and no two members of a given structure can be assigned to the same Offset.

Meaning

RLSL needs to explicit decorate members of a struct with the correct offset. That offset needs to follow the rules defined in std140, and this only applies to PushConstant and Uniform.

Implementation

Idea 1

RLSL can easily generate the correct offset from the rules from std140 but then the Rust and RLSL code would get out of sync.

One idea would be to only check if the struct is conformant with the rules from std140. Then RLSL would just extract the offset that was defined by the user.

Generally annotation of structs doesn't seem reasonable, for example you don't want your Vec3<f32> to be 16 bytes wide in the vertex input. Either accept the performance hit, or create separate types for the all the uniforms.

Idea 2

#[derive(Packed)]
pub struct SomeStruct{
    pub v: Vec4<f32>,
    pub f: f32
}

// generated
fn compute_packed(&self) -> Packed<SomeStruct>{..}

Compute the correct layout inside RLSL and implement Packed in normal Rust as a custom derive.

It would then know how to serialize the specified struct with the rules of std140.
And Packed could look like this

struct Packed<T>{
    _m: PhantomData<T>,
    data: Vec<u8>
}

// Hopefully the max size can be computed statically
struct Packed<T: Uniform>{
    _m: PhantomData<T>,
    data: [u8; T::Size]
}

For example if you have

let uniforms: Vec<SomeStruct> = ..;

And if you want to send it to the shader you have to pack them correctly

for packed_struct in uniforms.iter().map(SomeStruct::compute_packed){
    // pseudo api
    some_shader.send(packed_struct);
    //draw
}

And inside RLSL you can just use SomeStruct directly.

I am unsure how exactly I could implement it because custom derive doesn't expose type information but I think there should be a way. I might have to use const_fn. Alternatively everything could be computed in a build script with the reflection api.

How do you currently handle alignment? What would be your preferred API?

Add a description

This repo deserves more attention. Add a description to attract more people!

New syntax for entry points

The current syntax is just a proof of concept but it is missing a lot of information. Two options come to mind

We use a macro. This macro then generates some code under the hood so that RLSL can retrieve the necessary information.

vertex!(
    input(location=0) pos_in: Vec4<f32>,
    input(location=1) color_in: Vec4<f32>,
    uniform(set = 0, binding = 0) foo: Foo,
    output(location=0, flat) mut color: Vec4<f32>,
    output(location=1) mut color: Vec4<f32>, // smooth default
    output(location=2, noperspective) mut color2: Vec4<f32> {
    }
)

Because macros are severally limited, so we pass everything explicitly with types. Alternatively I could generate many custom attributes with a proc_macro.

The more explicit alternative would be.

type VertexOut =(Output<Flat, 0, Vec4<f32>>,
                 Output<1, Vec4<f32>>,
                 Output<NoPersp, 2, Vec4<f32>>);

#[spirv(vertex)]
fn vertex(pos_in: Input<0, Vec4<f32>>,
          color_in: Input<1, Vec4<f32>>,
          foo: Descriptor<0, 0, Foo>) -> VertexOut {

}

I think under the hood, I need to pass types anyways. Also currently Rust doesn't have const generics so the code above would look like

type VertexOut =(Output<Flat, N0, Vec4<f32>>,
                 Output<N1, Vec4<f32>>,
                 Output<NoPersp, N2, Vec4<f32>>);

#[spirv(vertex)]
fn vertex(pos_in: Input<N0, Vec4<f32>>,
          color_in: Input<N1, Vec4<f32>>,
          foo: Descriptor<N0, N0, Foo>) -> VertexOut {

}

Where the constants will be defined like this

#[spirv(CONST0)]
pub struct N0;
#[spirv(CONST1)]
pub struct N1;
#[spirv(CONST2)]
pub struct N2;
...

I am currently in favor of the function like syntax with explicit types.

Note: I also want do redesign PerVertex which is why it doesn't appear in this issue.

What are your thoughts? Any feedback is greatly appreciated. Of course if you have a completely different idea, I would like to hear it.

Allow functions to return references

SPIR-V only allowes to return values by copy. #[inline(always)] in Rust doesn't seem to force inlining.

This is a needed feature because a lot of things in Rust like ops::Index and ops::Deref return a reference.

A temporary workaround would be to implement an intrinsic for accessing elements in a storage buffer.

A possible implementation could be to transform functions from

fn index(&self, idx: usize) -> &Foo{..}

to

fn index(&self, idx: usize, output: &mut Foo){..}

Possibly those functions would need to be annotated with something like

#[spirv(transform_ref)]
fn index(&self, idx: usize) -> &Foo{..}

Another possibility would be to force inlining, by inserting those functions directly into the mir where they are used.

Specialization constants

My first idea was

#[spirv(fragment)]
fn frag(color: Vec4<f32>, c: Constant<Foo>) -> Vec4<f32> {
    color
}

but that doesn't work because Rust does not have default arguments.

#[spirv(fragment)]
fn frag(color: Vec4<f32>, ) -> Vec4<f32> {
    const c: Constant<Foo> = ...;
    color
}

That doesn't work because const is compiled away.

static FOO: Foo = ..;
#[spirv(fragment)]
fn frag(color: Vec4<f32>, ) -> Vec4<f32> {
    let f = FOO;
    color
}

That should work but it is too easy to add and remove statics inside an entry point. Commenting out let f = FOO; would change the shader signature. Maybe it is not too bad?


#[spirv(fragment)]
fn frag(color: Vec4<f32>, ) -> Vec4<f32> {
    static FOO: Foo = ..;
    let f = FOO;
    color
}

Maybe statics should be defined inside the entry point? But that also doesn't feel quite right.

static FOO: Foo = ..;
#[spirv(fragment)]
#[spirv(constant = FOO)]
fn frag(color: Vec4<f32>, ) -> Vec4<f32> {
    let f = FOO; // Now FOO is a specialization constant
    color
}

Maybe specialization constants should be added as an attribute?

#[spirv(fragment)]
fn frag(color: Vec4<f32>, c: Constant<Foo>) -> Vec4<f32> {
    color
}

Maybe this still works if I force Constants to implement Default or a similar trait? that doesn't seem straight forward to implement. I would have to evaluate it manually with miri.

Syntax for PerVertex

Currently this is how PerVertex is implemented

#[spirv(vertex)]
fn vertex(vertex: &mut Vertex, pos: Vec4<f32>, color: Vec4<f32>) -> Vec4<f32> {
    vertex.position = pos;
    color
}

An alternative would be to output the PerVertex information

#[spirv(vertex)]
fn vertex(pos: Vec4<f32>, color: Vec4<f32>) -> (Vertex, Vec4<f32>) {
    let mut vertex = Vertex::new(..); 
    vertex.position = pos;
    (vertex, color)
}

Then there are other builtin variables

in int gl_VertexID;
in int gl_InstanceID;
in int gl_DrawID; // Requires GLSL 4.60 or ARB_shader_draw_parameters
in int gl_BaseVertex; // Requires GLSL 4.60 or ARB_shader_draw_parameters
in int gl_BaseInstance; // Requires GLSL 4.60 or ARB_shader_draw_parameters
#[spirv(VertexInput)]
pub struct VertexIn{
     instance_id: i32,
     ....
}

#[spirv(vertex)]
fn vertex(input: VertexIn, pos: Vec4<f32>, color: Vec4<f32>) -> (Vertex, Vec4<f32>) {
    let vertex = Vertex::new(..);
    vertex.position = pos;
    (vertex, color)
}

Investigate recent ICE on nightly

error: internal compiler error: librustc/infer/canonical.rs:695: failed to lift QueryResult { var_values: CanonicalVarValues { var_values: ['?0, '?1, '?2] }, region_constraints: [Binder(OutlivesPredicate('?0, '?0)), Binder(OutlivesPredicate('?0, '?0)), Binder(OutlivesPredicate('?0, '?0)), Binder(OutlivesPredicate('?0, '?0)), Binder(OutlivesPredicate(ReStatic, '?2))], certainty: Proven, value: NormalizationResult { normalized_ty: Input<'_> } }, canonicalized from QueryResult { var_values: CanonicalVarValues { var_values: ['_#0r, '_#1r, '_#2r] }, region_constraints: [Binder(OutlivesPredicate('_#0r, '_#3r)), Binder(OutlivesPredicate('_#0r, '_#4r)), Binder(OutlivesPredicate('_#3r, '_#0r)), Binder(OutlivesPredicate('_#4r, '_#0r)), Binder(OutlivesPredicate(ReStatic, '_#2r))], certainty: Proven, value: NormalizationResult { normalized_ty: Input<'_> } }

Fix main function symbol in the linker

rlsl currently still tries to link with the main

error: linking with `cc` failed: exit code: 1
  |
  = note: "cc" "-Wl,--as-needed" "-Wl,-z,noexecstack" "-m64" "-L" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib" "/home/maik/projects/rlsl/rlsl-example/target/debug/deps/rlsl_example-0ed607f60771a702.1y16o1qfye96o7m0.rcgu.o" "-o" "/home/maik/projects/rlsl/rlsl-example/target/debug/deps/rlsl_example-0ed607f60771a702" "/home/maik/projects/rlsl/rlsl-example/target/debug/deps/rlsl_example-0ed607f60771a702.crate.allocator.rcgu.o" "-Wl,--gc-sections" "-pie" "-Wl,-z,relro,-z,now" "-nodefaultlibs" "-L" "/home/maik/projects/rlsl/rlsl-example/target/debug/deps" "-L" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib" "-Wl,-Bstatic" "/home/maik/projects/rlsl/rlsl-example/target/debug/deps/librlsl_math-b58264b8e9da687f.rlib" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd-57fdceb1c52b4db2.rlib" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib/libpanic_unwind-4f85ba5d0e870e29.rlib" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc_jemalloc-a655730befa35987.rlib" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib/libunwind-c86c9565da689e14.rlib" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc_system-655151fba596847e.rlib" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib/liblibc-b8f9bb8294d9a014.rlib" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib/liballoc-513d34708cb20443.rlib" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib/libstd_unicode-5211f032242a5357.rlib" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcore-e2f49b08d2bc06b5.rlib" "/home/maik/.rlsl/compiler/lib/rustlib/x86_64-unknown-linux-gnu/lib/libcompiler_builtins-136e26942e0df602.rlib" "-Wl,-Bdynamic" "-l" "dl" "-l" "rt" "-l" "pthread" "-l" "pthread" "-l" "gcc_s" "-l" "c" "-l" "m" "-l" "rt" "-l" "pthread" "-l" "util" "-l" "util"
  = note: /usr/lib/gcc/x86_64-pc-linux-gnu/7.2.1/../../../../lib/Scrt1.o: In function `_start':
          (.text+0x20): undefined reference to `main'
          collect2: error: ld returned 1 exit status

Implement intrinsic types through traits

pub trait SpirvVector {
    type Intrinsic: Intrinsic;
}

unsafe trait Intrinsic {}

// This type has the metainformation
#[spirv(Vector3)]
struct SpirvVec3;

unsafe impl Intrinsic for SpirvVec3{}

impl SpirvVector for SomeExternalMathVec3 {
    type Intrinsic = SpirvVec3;
}

// Check if a given type implements SpirvVector, if it does then access the Intrinsic type which
// has the metainformation to generate a specific OpVector type.

// Some pseudo code

let trait_impl: Option<TraitImpl> = tcx.get_impl_of_trait(ty, def_id);
let associated_ty: Option<Ty> = trait_impl.and_then(|trait_impl| trait_impl.associated_types_iter().first());

fn type_moves_by_default should be enough to figure out how trait system works in rustc

A logo for rlsl

I would appreciate it very much if anyone would like to design a logo for rlsl as I am not a very good artist myself.

I am also working on a GPU path tracer with a friend, maybe I can create some nice looking fractals in the future that could be used as a logo.

Optimize references away and allow mutable references inside functions

layout(location = 0) in vec2 a_Pos;

void test(inout vec2 v) {
    v = vec2(1,1);
}

void main() {
    vec2 v = a_Pos;
    test(v);
    gl_Position = vec4(v, 0.0, 1.0);
}

glslang always creates a new variable for a param, and then directly passes that variable.

        %v_0 = OpVariable %_ptr_Function_v2float Function
      %param = OpVariable %_ptr_Function_v2float Function
         %29 = OpLoad %v2float %a_Pos
               OpStore %v_0 %29
         %30 = OpLoad %v2float %v_0
               OpStore %param %30
         %31 = OpFunctionCall %void %test_vf2_ %param
         %32 = OpLoad %v2float %param
               OpStore %v_0 %32

This is because variables are always mutable, and changes inside a function would affect the value of the params.

I assume if one param was marked as inout glslang just stores the value of the param in the variable that was marked inout

         %32 = OpLoad %v2float %param
               OpStore %v_0 %32

In rlsl I don't pass variables directly, I only pass the load.

         %23 = OpVariable %_ptr_Function__ptr_Function_float Function
      %coord = OpVariable %_ptr_Function_v3float Function
         %26 = OpVariable %_ptr_Function_float Function
         %27 = OpVariable %_ptr_Function_v3float Function
         %30 = OpVariable %_ptr_Function__ZN4core6marker11PhantomData Function
               OpBranch %16
         %16 = OpLabel
               OpStore %f %float_1
               OpStore %23 %f
         %32 = OpLoad %_ptr_Function_float %23
         %33 = OpFunctionCall %void %_ZN6shader8test_mut %32

If something is marked &mut, I'll just pass the variable directly instead of passing the loaded value.

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.