FShade


Types

FShade translates .NET types to GLSL types using a simple scheme which is outlined here.

Due to limitations in GLSL various .NET constructs cannot be translated and FShade will simply fail (or produce invalid code) when trying to do so. However when used with care custom types can be quite helpful in FShade.

Records

F#'s record types get translated to simple structs and may therefore not be recursive in any way.

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
type MyRecord = { a : V4d; b : float }

let shader1 (v : Vertex) =
    vertex {
        let test = { a = v.pos; b = v.color.W }
        return { v with color = test.b * v.color }
    }

printShader shader1
#version 410

struct FSI_0131_MyRecord
{
    vec4 a;
    float b;
};

FSI_0131_MyRecord new_FSI_0131_MyRecord(vec4 a, float b)
{
    FSI_0131_MyRecord res;
    res.a = a;
    res.b = b;
    return res;
}

#ifdef Vertex
layout(location = 0) in vec4 Colors;
layout(location = 1) in vec4 Positions;
layout(location = 0) out vec4 ColorsOut;
layout(location = 1) out vec4 PositionsOut;
void main()
{
    ColorsOut = (new_FSI_0131_MyRecord(Positions, Colors.w).b * Colors);
    PositionsOut = Positions;
}

#endif

As you see here the resulting code defines the type itself as a GLSL struct and a constructor-function creating an instance of the type. The use of records is therefore relatively straight-forward as long as no rendering-engine interop is required (custom uniform-types, etc.)

Discriminated Unions

Union types are not that easy to translate, since GLSL does not provide mechanisms for encoding them directly. Therefore FShade translates union types to structs carrying all fields for each case and a special field holding a tag representing the constructor.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
18: 
type MyUnion =
    | Case1 of col : V4d
    | Case2 of factor : int
    | Case3

let shader2 (v : Vertex) =
    vertex {
        let test = Case1 v.color
        match test with
            | Case1 a ->         
                return { v with color = a }
            | Case2 b ->
                return { v with color = float b * v.color }
            | Case3 ->
                return { v with color = V4d.IIII }
    }

printShader shader2
#version 410

struct FSI_0135_MyUnion
{
    int tag;
    vec4 Case1_col;
    int Case2_factor;
};

FSI_0135_MyUnion new_FSI_0135_MyUnion_Case1(vec4 col)
{
    FSI_0135_MyUnion res;
    res.tag = 0;
    res.Case1_col = col;
    return res;
}

#ifdef Vertex
layout(location = 0) in vec4 Colors;
layout(location = 1) in vec4 Positions;
layout(location = 0) out vec4 ColorsOut;
layout(location = 1) out vec4 PositionsOut;
void main()
{
    FSI_0135_MyUnion test = new_FSI_0135_MyUnion_Case1(Colors);
    if((test.tag == 1))
    {
        ColorsOut = (float(test.Case2_factor) * Colors);
        PositionsOut = Positions;
    }
    else
    {
        if((test.tag == 2))
        {
            ColorsOut = vec4(1.0, 1.0, 1.0, 1.0);
            PositionsOut = Positions;
        }
        else
        {
            ColorsOut = test.Case1_col;
            PositionsOut = Positions;
        }
    }
}

#endif

Arrays

Since F# does not include a type for fixed-size arrays we decided to define our own type using type providers for the size. That ways array types can easily be written like:

1: 
type MyArray = Arr<4 N, float>

Here's a little shader calculating the sum of an array

1: 
2: 
3: 
4: 
5: 
6: 
7: 
8: 
9: 
let shader3 (v : Vertex) =
    vertex {
        let arr : Arr<16 N, V4d> = uniform?SomeArray
        let mutable sum = V4d.Zero
        for i in 0 .. arr.Length - 1 do
            sum <- sum + arr.[i]
        return { v with color = sum }       
    }
printShader shader3
#version 410

layout(std140)
uniform Global
{
    vec4 SomeArray[16];
};

#ifdef Vertex

layout(location = 0) in vec4 Positions;
layout(location = 0) out vec4 ColorsOut;
layout(location = 1) out vec4 PositionsOut;
void main()
{
    vec4 arr[16] = SomeArray;
    vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
    for(int i = 0; (i < 16); i++)
    {
        sum = (sum + arr[i]);
    }
    ColorsOut = sum;
    PositionsOut = Positions;
}

#endif

Samplers

Shaders often need samplers for texture-lookups and similar uses. FShade defines its own types for samplers, which are rather similar to the ones defined by GLSL.

 1: 
 2: 
 3: 
 4: 
 5: 
 6: 
 7: 
 8: 
 9: 
10: 
11: 
12: 
13: 
14: 
15: 
16: 
17: 
let sammy =
    sampler2d {
        texture uniform?MyTexture
        addressU WrapMode.Wrap
        addressV WrapMode.Clamp
        filter Filter.MinMagMipLinear
        minLod 1.5
        maxLod 10.0
        mipLodBias 0.5
    }

let shader4 (v : Vertex) =
    vertex {
        return { v with color = sammy.Sample(v.pos.XY) }
    }

printShader shader4
#version 410

uniform sampler2D sammy;

#ifdef Vertex

layout(location = 0) in vec4 Positions;
layout(location = 0) out vec4 ColorsOut;
layout(location = 1) out vec4 PositionsOut;
void main()
{
    ColorsOut = texture(sammy, Positions.xy);
    PositionsOut = Positions;
}

#endif
namespace Microsoft
namespace Microsoft.FSharp
namespace Microsoft.FSharp.Quotations
namespace Aardvark
namespace Aardvark.Base
namespace FShade
namespace FShade.Imperative
module Utilities
type Vertex =
  {pos: V4d;
   color: V4d;}
Multiple items
type PositionAttribute =
  inherit SemanticAttribute
  new : unit -> PositionAttribute

--------------------
new : unit -> PositionAttribute
Vertex.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)
Multiple items
type SemanticAttribute =
  inherit Attribute
  new : s:string -> SemanticAttribute
  member Semantic : string

--------------------
new : s:string -> SemanticAttribute
Vertex.color: V4d
type MyRecord =
  {a: V4d;
   b: float;}
MyRecord.a: V4d
MyRecord.b: float
Multiple items
val float : value:'T -> float (requires member op_Explicit)

--------------------
type float = System.Double

--------------------
type float<'Measure> = float
val shader1 : v:Vertex -> Expr<Vertex>
val v : Vertex
val vertex : VertexBuilder
val test : MyRecord
field V4d.W: float
val printShader : a:('a -> Expr<'b>) -> unit
type MyUnion =
  | Case1 of col: V4d
  | Case2 of factor: int
  | Case3
union case MyUnion.Case1: col: V4d -> MyUnion
union case MyUnion.Case2: factor: int -> MyUnion
Multiple items
val int : value:'T -> int (requires member op_Explicit)

--------------------
type int = int32

--------------------
type int<'Measure> = int
union case MyUnion.Case3: MyUnion
val shader2 : v:Vertex -> Expr<Vertex>
val test : MyUnion
val a : V4d
val b : int
field V4d.IIII: V4d
type MyArray = Arr<N<...>,float>
Multiple items
module Arr

from Aardvark.Base.Arrays

--------------------
module Arr

from Aardvark.Base

--------------------
type Arr<'d,'a (requires 'd :> INatural)> =
  interface IEnumerable<'a>
  new : unit -> Arr<'d,'a>
  new : elements:seq<'a> -> Arr<'d,'a>
  member AsString : string
  member Data : 'a []
  member Item : i:int -> 'a with get
  member Length : int
  member Item : i:int -> 'a with set

--------------------
new : unit -> Arr<'d,'a>
new : elements:seq<'a> -> Arr<'d,'a>
type N
val shader3 : v:Vertex -> Expr<Vertex>
Multiple items
val arr : Arr<N<...>,V4d>

--------------------
type arr<'a> =
  private {root: FingerTreeNode<'a,int>;}
    interface IEnumerable<'a>
    interface IEnumerable
    member private AsString : string
    member Item : i:int -> 'a with get
    member Length : int
val uniform : UniformScope
val mutable sum : V4d
field V4d.Zero: V4d
val i : int
val sammy : Sampler2d
val sampler2d : Sampler2dBuilder
custom operation: texture (ShaderTextureHandle)

Calls SamplerBaseBuilder.Texture
custom operation: addressU (WrapMode)

Calls SamplerBaseBuilder.AddressU
type WrapMode =
  | Wrap = 0
  | Mirror = 1
  | Clamp = 2
  | Border = 3
  | MirrorOnce = 4
WrapMode.Wrap: WrapMode = 0
custom operation: addressV (WrapMode)

Calls SamplerBaseBuilder.AddressV
WrapMode.Clamp: WrapMode = 2
custom operation: filter (Filter)

Calls SamplerBaseBuilder.Filter
type Filter =
  | Anisotropic = 0
  | MinLinearMagMipPoint = 1
  | MinLinearMagPointMipLinear = 2
  | MinMagLinearMipPoint = 3
  | MinMagMipLinear = 4
  | MinMagMipPoint = 5
  | MinMagPointMipLinear = 6
  | MinPointMagLinearMipPoint = 7
  | MinPointMagMipLinear = 8
  | MinMagPoint = 9
  | MinMagLinear = 10
  | MinPointMagLinear = 11
  | MinLinearMagPoint = 12
Filter.MinMagMipLinear: Filter = 4
custom operation: minLod (float)

Calls SamplerBaseBuilder.MinLod
custom operation: maxLod (float)

Calls SamplerBaseBuilder.MaxLod
custom operation: mipLodBias (float)

Calls SamplerBaseBuilder.MipLodBias
val shader4 : v:Vertex -> Expr<Vertex>
Fork me on GitHub