FShade


FShade: first-class shaders for F#

FShade is a library that extends F# with a domain specific language for shaders. It provides high-level utilities for working with first-class shaders inlcuding composition-, modification- and preprocessing-tools. It focuses on graphics shaders but also provides reusable utilities for compiling F# functions to other c-like languages.

FShade uses primitive vector-/matrix-types from Aardvark.Base for representing their shader-language counterparts but could (on demand) be extended to use others too.

Example

This example implements a very basic vertex shader that can be compiled using FShade.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
type Data = { pos : V4d; tc : V2d }

let shader (d : Data) =
    vertex {
        let mvp : M44d = uniform?ModelViewProjMatrix
        return { pos = mvp * d.pos; tc = 2.0 * d.tc }
    }

Effects

FShade represents shader code using Effect which assigns shader code to the available pipeline stages. The available stages are the ones currently available in modern graphics APIs:

  • Vertex
  • TessControl
  • TessEval
  • Geometry
  • Fragment

Each Effect can provide code for a subset of these stages and can be composed with others using Effect.compose as described in Composition. Effect also exposes some other functions for creating/transforming/inspecting effects.

The shader from above can be translated to an effect by using

1: 
let effect = Effect.ofFunction shader

Compilation

FShade's compilation process consists of three stages which successively lower the abstraction level.

Module

Module serves as the common interface for FShade's compiler and provides a list of entry-point definitions (using F#'s Expr) Effect.toModule translates an Effect to one of these.

CModule

CModules represent the content of c-files which can consist or various value-/type-definitions. In order to compile a Module one can use ModuleCompiler. Note that in this step needs information about intrinsic functions. Therefore ModuleCompiler.compile requires a Backend as argument which knows about the intrinsic (predefined) functions and types for the current target language.

Assembler

FShade defines a GLSL 'Assembler' that simply transforms a CModule to GLSL code. This is done by simply visiting the entire CModule while emitting appropriate c-code. This way assemblers don't need to stick to any specific interface and other assemblers may produce other output-types. For example SpirV would need to produce byte[] instead of strings.

Here's an example compiling an effect to GLSL illustrating the compiler-stages.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
effect
    // Effect -> Module (the config specifies desired outputs, etc.)
    |> Effect.toModule { 
        EffectConfig.empty with 
            lastStage = ShaderStage.Vertex
            outputs = Map.ofList ["pos", (typeof<V4d>, 0)]
        }

    // Module -> CModule
    |> ModuleCompiler.compile glsl410

    // CModule -> string
    |> GLSL.Assembler.assemble glsl410  

    // show the code
    |> printfn "%s"
#version 410

layout(std140)
uniform Global
{
    mat4x4 ModelViewProjMatrix;
};

#ifdef Vertex

layout(location = 0) in vec4 pos;
layout(location = 0) out vec4 posOut;
void main()
{
    posOut = (ModelViewProjMatrix * pos);
}

#endif
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Quotations
namespace Aardvark
namespace Aardvark.Base
namespace FShade
namespace FShade.Imperative
module Utilities
Multiple items
namespace Microsoft.FSharp.Data

--------------------
type Data =
  {pos: V4d;
   tc: V2d;}
Data.pos: V4d
Multiple items
type V4d =
  struct
    new : v:int -> V4d + 25 overloads
    val X : float
    val Y : float
    val Z : float
    val W : float
    member Abs : V4d
    member AllDifferent : v:V4d -> bool + 1 overload
    member AllEqual : v:V4d -> bool + 1 overload
    member AllGreater : v:V4d -> bool + 1 overload
    member AllGreaterOrEqual : v:V4d -> bool + 1 overload
    ...
  end

--------------------
V4d ()
   (+0 other overloads)
V4d(v: int) : V4d
   (+0 other overloads)
V4d(a: int []) : V4d
   (+0 other overloads)
V4d(v: int64) : V4d
   (+0 other overloads)
V4d(a: int64 []) : V4d
   (+0 other overloads)
V4d(v: float32) : V4d
   (+0 other overloads)
V4d(a: float32 []) : V4d
   (+0 other overloads)
V4d(v: float) : V4d
   (+0 other overloads)
V4d(a: float []) : V4d
   (+0 other overloads)
V4d(index_fun: System.Func<int,float>) : V4d
   (+0 other overloads)
Data.tc: V2d
Multiple items
type V2d =
  struct
    new : v:int -> V2d + 20 overloads
    val X : float
    val Y : float
    member Abs : V2d
    member AllDifferent : v:V2d -> bool + 1 overload
    member AllEqual : v:V2d -> bool + 1 overload
    member AllGreater : v:V2d -> bool + 1 overload
    member AllGreaterOrEqual : v:V2d -> bool + 1 overload
    member AllInfinity : bool
    member AllNaN : bool
    ...
  end

--------------------
V2d ()
   (+0 other overloads)
V2d(v: int) : V2d
   (+0 other overloads)
V2d(a: int []) : V2d
   (+0 other overloads)
V2d(v: int64) : V2d
   (+0 other overloads)
V2d(a: int64 []) : V2d
   (+0 other overloads)
V2d(v: float32) : V2d
   (+0 other overloads)
V2d(a: float32 []) : V2d
   (+0 other overloads)
V2d(v: float) : V2d
   (+0 other overloads)
V2d(a: float []) : V2d
   (+0 other overloads)
V2d(index_fun: System.Func<int,float>) : V2d
   (+0 other overloads)
val shader : d:Data -> Expr<Data>
val d : Data
val vertex : VertexBuilder
val mvp : M44d
Multiple items
type M44d =
  struct
    new : a:float[] -> M44d + 2 overloads
    val M00 : float
    val M01 : float
    val M02 : float
    val M03 : float
    val M10 : float
    val M11 : float
    val M12 : float
    val M13 : float
    val M20 : float
    ...
  end

--------------------
M44d ()
M44d(a: float []) : M44d
M44d(a: float [], start: int) : M44d
M44d(m00: float, m01: float, m02: float, m03: float, m10: float, m11: float, m12: float, m13: float, m20: float, m21: float, m22: float, m23: float, m30: float, m31: float, m32: float, m33: float) : M44d
val uniform : UniformScope
val effect : Effect
Multiple items
module Effect

from Utilities

--------------------
module Effect

from FShade

--------------------
type Effect =
  new : m:Lazy<Map<ShaderStage,Shader>> -> Effect
  new : m:Lazy<Map<ShaderStage,Shader>> * o:Effect list -> Effect
  private new : id:string * shaders:Lazy<Map<ShaderStage,Shader>> * composedOf:Effect list -> Effect
  member ComposedOf : Effect list
  member FirstShader : Shader option
  member FragmentShader : Shader option
  member GeometryShader : Shader option
  member Id : string
  member InputToplogy : InputTopology option
  member Inputs : Map<string,Type>
  ...

--------------------
new : m:System.Lazy<Map<ShaderStage,Shader>> -> Effect
new : m:System.Lazy<Map<ShaderStage,Shader>> * o:Effect list -> Effect
val ofFunction : shaderFunction:('a -> Expr<'b>) -> Effect
val toModule : config:EffectConfig -> effect:Effect -> Module
Multiple items
module EffectConfig

from FShade

--------------------
type EffectConfig =
  {depthRange: Range1d;
   flipHandedness: bool;
   lastStage: ShaderStage;
   outputs: Map<string,(Type * int)>;}
val empty : EffectConfig
Multiple items
module ShaderStage

from Aardvark.Base

--------------------
type ShaderStage =
  | Vertex = 0
  | TessControl = 1
  | TessEval = 2
  | Geometry = 3
  | Fragment = 4
  | Compute = -1
Multiple items
ShaderStage.Vertex: ShaderStage = 0

--------------------
ShaderStage.Vertex: ShaderStage = 1
Multiple items
module Map

from FShade.BasicQuotationPatterns

--------------------
module Map

from FShade.StateExtensions

--------------------
module Map

from Aardvark.Base.Prelude

--------------------
module Map

from Aardvark.Base.Predefined Lenses

--------------------
module Map

from Microsoft.FSharp.Collections

--------------------
type Map<'Key,'Value (requires comparison)> =
  interface IEnumerable
  interface IComparable
  interface IEnumerable<KeyValuePair<'Key,'Value>>
  interface ICollection<KeyValuePair<'Key,'Value>>
  interface IDictionary<'Key,'Value>
  new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
  member Add : key:'Key * value:'Value -> Map<'Key,'Value>
  member ContainsKey : key:'Key -> bool
  override Equals : obj -> bool
  member Remove : key:'Key -> Map<'Key,'Value>
  ...

--------------------
new : elements:seq<'Key * 'Value> -> Map<'Key,'Value>
val ofList : elements:('Key * 'T) list -> Map<'Key,'T> (requires comparison)
val typeof<'T> : System.Type
Multiple items
module ModuleCompiler

from Utilities

--------------------
module ModuleCompiler

from FShade.Imperative

--------------------
module ModuleCompiler

from FShade.SpirV Extensions

--------------------
module ModuleCompiler

from FShade.Backends
val compile : backend:Compiler.Backend -> m:Module -> CModule
val glsl410 : GLSL.Backend
Multiple items
module GLSL

from Utilities

--------------------
namespace FShade.GLSL
Multiple items
module Assembler

from Utilities.GLSL

--------------------
module Assembler

from FShade.GLSL
Multiple items
val assemble : cfg:GLSL.Backend -> s:CModule -> string

--------------------
val assemble : backend:GLSL.Backend -> m:CModule -> GLSL.GLSLShader
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
Fork me on GitHub