Skip to content

Latest commit

 

History

History
137 lines (102 loc) · 4.01 KB

DESIGN.md

File metadata and controls

137 lines (102 loc) · 4.01 KB

Design Guidelines

  • Granularity. One function per file. The file and the function share the same name, namely: myFunc.glsl contains myFunct(). There are some files that just include a collection of files inside a folder with the same name. For example:
    color/blend.glsl
    // which includes
    color/blend/*.glsl

  • Multi-language. Right now, most of the code is GLSL (*.glsl) and HLSL (*.hlsl), but we are slowly extending to WGSL (*.wgsl), CUDA (*.cuh) and Metal (*.msl).
    math/mirror.glsl
    math/mirror.hlsl
    math/mirror.wgsl
    math/mirror.msl
    math/mirror.cuh
  • Self documented. Each file contains a structured comment (in YAML) at the top of the file. This one contains the name of the original author, description, use, #define options, and any implementation-specific notes.
    /*
    contributors: <FULL NAME>
    description: [DESCRIPTION + URL]
    use: <vec2> myFunc(<vec2> st, <float> x [, <float> y])
    notes:
        - The option MYFUNC_TYPE is not supported on WGSL.
    options:
        - MYFUNC_TYPE
        - MYFUNC_SAMPLER_FNC()
    */
  • Prevent name collisions by using the following pattern where FNC_ is followed with the function name:
    #ifndef FNC_MYFUNC
    #define FNC_MYFUNC

    float myFunc(float in) {
        return in;
    }

    #endif
  • Templating capabilities through #defines. Probably the most frequent use is templating the sampling function for reusability. The #define options start with the name of the function, in this example MYFUNC_. They are added as options: in the header.
    #ifndef MYFUNC_TYPE
    #define MYFUNC_TYPE vec4
    #endif

    #ifndef MYFUNC_SAMPLER_FNC
    #define MYFUNC_SAMPLER_FNC(TEX, UV) texture2D(TEX, UV)
    #endif

    #ifndef FNC_MYFUNC
    #define FNC_MYFUNC
    MYFUNC_TYPE myFunc(SAMPLER_TYPE tex, vec2 st) {
        return MYFUNC_SAMPLER_FNC(tex, st);
    }
    #endif
  • Argument order. Optional elements are at the end. When possible sort them according their memory footprint (except textures that remain at the top). Ex.: SAMPLER_TYPE, mat4, mat3, mat2, vec4, vec3, vec2, float, ivec4, ivec3, ivec2, int, bool
    /*
    ...
    use: myFunc(<vec2> st, <vec2|float> x[, <float> y])
    */

    #ifndef FNC_MYFUNC
    #define FNC_MYFUNC
    vec2 myFunc(vec2 st, vec2 x) {
        return st * x;
    }

    vec2 myFunc(vec2 st, float x) {
        return st * x;
    }

    vec2 myFunc(vec2 st, float x, float y) {
        return st * vec2(x, y);
    }
    #endif

WGSL Specifics

WGSL as a language has some fundamental differences from GLSL, HLSL and METAL. Here are some guidelines to help with the transition:

  • WGSL Function Renaming. WGSL does not support function overloading and as such function names must be unique and should reflect the size of parameter, return types. See documented examples below.
    // When the parameter types and return type is consistent and scalar, no suffix is needed.
    fn random(p: f32) -> f32 { ... }

    /*
    When only the return type is consistent and scalar, the function name is suffixed based on the size of the parameter types, i.e.
      - vec2<T> -> 2
      - vec3<T> -> 3
      - vec4<T> -> 4
    */
    fn random2(p: vec2f) -> f32 { ... }
    fn random3(p: vec3f) -> f32 { ... }
    fn random3(p: vec4f) -> f32 { ... }

    /* 
    When both parameters and return types are inconsistent, function name has two suffixes:
      - first for return type size
      - second for parameter type size
    */
    fn random21(p: f32) -> vec2f { ... }
    fn random22(p: vec2f) -> vec2f { ... }
    fn random23(p: vec3f) -> vec2f { ... }
    fn random31(p: f32) -> vec3f { ... }
    fn random32(p: vec2f) -> vec3f { ... }
    fn random33(p_: vec3f) -> vec3f { ... }
    fn random41(p: f32) -> vec4f { ... }
    fn random42(p: vec2f) -> vec4f { ... }
    fn random43(p: vec3f) -> vec4f { ... }
    fn random44(p: vec4f) -> vec4f { ... }