xshade-lang / xshade

"cross shade" meta shading language and compiler
MIT License
38 stars 3 forks source link

xshade design decisions and similar projects #58

Open csabahruska opened 6 years ago

csabahruska commented 6 years ago

Hi!

Are you familiar with other projects targeting GPU graphics using functional programming approach? i.e.

How is your system different?

Vengarioth commented 6 years ago

Hello Csaba, thanks for reaching out!

We are still very much in the "finding our niche" phase.

Before we started the project we were looking for a more modern GLSL/HLSL that could target DirectX, Metal, Vulkan and OpenGL. We did some research back then to find and evaluate existing solutions but found that many only target the OpenGL spectrum. Around that time Khronos published SPIR-V tools, to optimize and cross compile SPIR-V to said platforms. So all we needed was a language targeting SPIR-V. After some discussion we agreed to start the journey to build a new language.

At first the language wasn't even meant to be functional in any capacity. But since both of us had never written a compiler before, we did a lot more research into type systems and found a nice set of tradeoffs in the functional space which hopefully allow us to get away with a simple implementation.

Now, since i never used lambdacube3d i cannot give a direct comparison. But as of now, we aim to build a language and compiler that:

Cheers,

Andy

u235axe commented 6 years ago

Hi Andy!

Can you elaborate on the type system you chose for this project? What aspects / compromises did you investigated for such a domain specific task? (if it is already explained in some paper or talk, I'd be happy with a link).

Bests, Daniel

csabahruska commented 6 years ago

I think it's the GPU feature set that affects the high level language design and not the underlying low level API. For example LambdaCube 3D will need minimal api change to support SPIR-V.

Do you plan to support multi-pass render algorithms in the language? Will your language include the draw calls or will it be just a shader language?

Vengarioth commented 6 years ago

Okay, I took a brief look at GRIN, it seems to overlap a lot with what I had in mind for the optimization and code generation process. But I will need some more time to digest it. A lot of things in the papers folder of grin seems very much relevant. I'll try to describe what we plan to do again without making comparisons:

After a project (not a program) is type checked, our compiler acts like a tracing JIT, starting at each program (shader) and produces a trace, thus linearizing and substituting each program. With linearization we mean to inline each function call and unroll each loop. With substitution we mean enrich the trace with type information (more later). To do this we impose limitations on the language itself, one of which is to not have unbound loops. Our type system has dependent types for integers to denote array or tensor sizes, thus encoding iteration length in the types themselves.

We also impose the limitation that a Program (a syntactical construct in our language, representing a single shader program) must fully specify its type signature. This allows us to have type elision (not duct typing) in the rest of the codebase. Meaning if you don't specify types for a function, the compiler will only derive a set of required traits (attributes of types) for parameters during type checking. A fn foo(a, b) { a + b } will have noted that a and b must be of the same type and implement the trait for addition. If the function is used by a program the compiler will treat it like fn foo<T>(a: T, b: T) -> T { return a + b; } and try to monomorphize a version for the given parameters during trace generation. This is possible because shaders are pure and programs have fully specified their type signatures, thus alowing type information to flow through the rest of the code without annotation. Type annotations can also be written out if one wants to limit this implicit polymorphism.

The last noteworthy thing is, since shaders are pure functions and values in our language are immutable (no assignment expression), one can track the "identity" of values throughout the program and formalize most optimisations as a graph reduction problem. The generated trace of a program already is an acyclic, directed graph and can easily be optimized globally.

The key inspiration to build the compiler this way came from Nada Amin's work. I recommend her talks Programming Should Eat Itself and Collapsing Towers of Interpreters. Also Into the Core - Squeezing Haskell into Nine Constructors by Simon Peyton Jones.

One last note on trace compillation. I think recursion can be implemented here since the trace will unroll it at compile time. The question is how to ensure it converges. My current plans are to have a MAX_RECURSION_COUNT together with an analysis if function arguments change, assuming that a recursive call with the same arguments will lead to infinite recursion.

Now about the Type System. What we want to run with is a fairly simple implementation of concepts from hindley-milner and other "classical" type systems without proving their properties (which is beyond my capabilities). We do this by literally currying and calling a type constructor during type checking and trace generation. Our general type constructor looks like fn construct_type(BaseType, Name, TypeParameters, ValueParameters). BaseType is the first step of the hierachy and can be a value of Function, ProductType for structs and tuples, and so on. If we partially apply the BaseType with ProductType to construct_type we get the type constructor for all product types. The next step is to partially apply it with the name of a given struct, giving us a type constructor for all variants of said struct. The process goes on for all type and value arguments passed to the type, giving us an implicit type hierachy starting from the Any type like:

TypeConstructor(Any, Any, Any, Any) [
    TypeConstructor(Value, Any, Any, Any) [
        TypeConstructor(Value, Fixed("f32"), Any, Any) [
            TypeConstructor(Value, Fixed("f32"), None, Any) [
                // f32
                Type(Value, Fixed("f32"), None, None)
            ]
        ],
        TypeConstructor(Value, Fixed("f64"), Any, Any) [
            TypeConstructor(Value, Fixed("f64"), None, Any) [
                // f64
                Type(Value, Fixed("f64"), None, None)
            ]
        ]
    ],
    TypeConstructor(Product, Any, Any, Any) [
        TypeConstructor(Product, Fixed("Vector"), Any, Any) [
            TypeConstructor(Product, Fixed("Vector"), Fixed([Fixed("f32")]), Any) [
                // Vector<f32; 2>
                Type(Product, Fixed("Vector"), Fixed([Fixed("f32")]), Fixed([Fixed(2)])),
                // Vector<f32; 3>
                Type(Product, Fixed("Vector"), Fixed([Fixed("f32")]), Fixed([Fixed(3)])),
                // Vector<f32; 4>
                Type(Product, Fixed("Vector"), Fixed([Fixed("f32")]), Fixed([Fixed(4)]))
            ],
            TypeConstructor(Product, Fixed("Vector"), Fixed([Fixed("f64")]), Any) [
                // Vector<f64; 2>
                Type(Product, Fixed("Vector"), Fixed([Fixed("f64")]), Fixed([Fixed(2)])),
                // Vector<f64; 3>
                Type(Product, Fixed("Vector"), Fixed([Fixed("f64")]), Fixed([Fixed(3)])),
                // Vector<f64; 4>
                Type(Product, Fixed("Vector"), Fixed([Fixed("f64")]), Fixed([Fixed(4)]))
            ]
        ]
    ]
]

@csabahruska: We only do shaders and have no assumptions on the runtime using them. We want to give others the best tools to implement things like surface shaders or multipass construct on top of our work but will have no cpu side runtime.

Cheers,

Andy

csabahruska commented 6 years ago

What you described reminds me to The Spiral Language.

Regarding the shader language the Spark shading language could be interesting.

Vengarioth commented 6 years ago

Yeah, probably nothing that hasn't been done before. What we want to improve on is the integration into existing rendering engines and make the onboarding easy for developers and tech artists. Marc and I both come from the games industry and at least I have to deal with ad-hoc solutions to shader cross compillation and graph based editors on a regular basis. Those solutions usually fall short on user experience in terms of error messages and usually are missing significant features, thus costing teams a lot of time to deal with. I can't say for sure if it's a lack of visibility, familiarity or not invented here syndrome that holds existing solutions back. Another issue is that existing solutions do too much on the CPU side, demanding a restructuring of the rendering pipeline. As freelancers we usually come late into projects when most things are set in stone and can't rewrite large portions of the existing code base. We think besides having a good implementation new a shading language needs to adress all those issues.

Cheers,

Andy

u235axe commented 6 years ago

By graph editors I guess you mean the experience provided by eg. Unity's or Unreal Engine's shader editors? Do you plan to devise a graphical representation/editor for your language?

I strongly believe that better graphical editors could be constructed by designing them around functional constructs. I'd be interested in your experiences and - if there are any specific observations on missing features - how graphical editors could be more efficient and user friendly.

calebwin commented 4 years ago

I just want to say - having a shader language that compiles directly to SPIR-V (without shaderc as a dependency) is definitely very useful for Rust programmers who want to do graphics.

@Vengarioth are compute shaders supported?

jarble commented 3 years ago

@calebwin, @Vengarioth xshade hasn't been updated in a few years, but there are some other Rust-to-SPIRV compilers (such as RustGPU) that are still being developed.

Vengarioth commented 3 years ago

@jarble yes, this project is sort of on-hold. I got more invested in wgpu and rust-gpu. If you're interested in that I highly recommend joining the embark discord: https://discord.gg/dAuKfZS

I might revive this project at some point after the pandemic..