Home

Awesome

Vinci : a toy ML shading language

Vinci is a toy ML functional shading language. This repository contains its dedicated compiler targeting SPIR-V, an intermediate representation of Vulkan shaders.

The language and the compiler were developed as a part of a master's thesis at the Faculty of Mathematics, Informatics and Mechanics of the University of Warsaw (MIM UW). The extended abstract of the thesis, prepared for ICFP 2021 SRC, can be found inside the docs directory.

Installation

The project uses Stack build tool to compile and manage dependencies.

Installing the compiler:

stack build
stack install

Stack will install the compiler in ~/.local/bin/vinci-lang-exe, it can be then run by calling vinci-lang-exe.

Usage

Assuming the compiler is installed in ~/.local/bin/, running

vinci-lang-exe examples/test_frag.vc > examples/test_frag.spvasm

will produce a SPIR-V module in a human-readable representation. It should be further compiled using spirv-as, part of the SPIR-V Tools that gets installed along Vulkan SDK:

spirv-as --target-env vulkan1.1 examples/test_frag.spvasm -o examples/test_frag.spv

spirv-as creates a SPIR-V binary module from the human-readable representation, that can be executed by a program using Vulkan API, like this one.

Example of Vinci compiler usage

Code examples

Vinci is similar to other languages from ML family. In contrast to those languages, Vinci does not support algebraic data types or pattern matching due to performance concerns and limitations of GPU programming. On the other hand, apart from expressions typical for toy ML languages — e.g., arithmetic expressions, let bindings, or tuples — it also introduces C-like structures (similar to records from functional programming) and tuple destructuring syntax.

Code examples of fragment and vertex shaders are to be found in the examples directory. The fragment shader example displays a moving checkerboard (shown in the GIF above), the vertex shader presented below flips the 2D object every moment:

# structure definitions omitted

let pi = 3.1415926538;;

let vert (u : Uniforms) (ins : VertIns) =
  let (r, g, b) = ins.inColor in
  let (x, y) = ins.inPosition in
  let t = 1.0 + sin (u.ubo.u_time - pi / 2.0) in
  let f1 x = x - t and f2 x = x + t in
  let y2 = if y > 0.0 then f1 y else f2 y in
  VertOuts {
    gl_Position = (x, y2, 0.0, 1.0),
    fragColor = (r, g, b, 1.0),
    fragTexCoord = ins.inTexCoord
  }
;;

Vinci does have a (limited) support for recursion in a form of tail recursion. For example, the code below uses recursive function to compute ceiled division in a very convoluted way:

let ceil_div x =
  let rec x i =
    if x < 0.2
    then (if 1.0 < i then 1.0 else i)
    else rec (x - 0.2) (i + 0.1)
  in rec x 0.0
;;

let frag (_ : Uniforms) (ins : FragIns) =
  let gray = ceil_div ins.fragColor.x in
  FragOuts {
    outColor = (gray, gray, gray, 1.0)
  }
;;

Recursive shader example

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

License

MIT