Home

Awesome

GPUC

A generic shading language compiler that writes metal, HLSL, and GLSL

GPUC is a work in progress, not ready for prime time.

The primary motivation was to create a header-only shader language translator that has negligible effect on compile times. Including this should not add more than a fraction of a second to your compile time, unlike other shader translators I tried.

Only the metal backend is actually useful right now, and probably only for simple shaders. The HLSL and GLSL backends have rotted a bit due to some recent changes in the source language.

I initially wrote this over a two week holiday vacation in December of 2017. I was amazed how easy it was to continue building upon when I returned to it many months later. For me, this was the first reasonably useful thing I implemented in C, and since then I have switched to C for the majority of my hobby projects.

Usage

Header-only library

// include API declarations as needed
#include "gpuc/gpuc.h"
// include implementation in one translation unit
#include "gpuc/gpuc.inl"

Stand-alone compiler

» sh gpuc.c --help
usage:

  gpuc [options] <source>

options:

  --help           display this usage summary
  --ast            print AST to stdout
  --debug          print debug summary to stdout
  --test           run tests
  --glsl           translate GPUC to GLSL
  --hlsl           translate GPUC to HLSL
  --metal          translate GPUC to Metal
  --frag <file>    write output to <file>, or '-' for stdout
  --vert <file>    write output to <file>, or '-' for stdout
» sh gpuc.c sample.gpuc --vert - --frag - --metal
#include <metal_stdlib>
#include <simd/simd.h>

using namespace metal;

float4 sample(const texture2d<float> t, float2 uv) {
    constexpr sampler s(filter::nearest);
    return t.sample(s, uv);
}

float4 sample(const texture2d<float> t, float2 uv, const sampler s) {
    return t.sample(s, uv);
}

struct gpuc {

    // float4 sample(texture2d, float2);

    // float4 sample(texture2d, float2, sampler);

    struct Camera {
        float4x4 mvp;
        float4x4 mvn;
    };

    struct Vertex {
        float3 position [[attribute(0)]];
        float4 color [[attribute(1)]];
        float4 texcoords [[attribute(2)]];
    };

    struct Fragment {
        float4 position [[position]];
        float4 color;
        float4 texcoords;
    };

    struct Sample {
        float4 color;
    };

    constant const Camera& cam;

    const texture2d<float> color;

    const sampler samp;

    Fragment vert(const Vertex v) const {
        Fragment f;
        f.position = cam.mvp * float4(v.position, 1);
        f.color = v.color;
        f.texcoords = v.texcoords;
        return f;
    }

};

vertex gpuc::Fragment vert(
    constant const gpuc::Camera& cam [[buffer(0)]],
    texture2d<float> color [[texture(0)]],
    sampler samp [[sampler(0)]],
    const gpuc::Vertex v [[stage_in]]
) {
    return gpuc{cam, color, samp}.vert(v);
}
#include <metal_stdlib>
#include <simd/simd.h>

using namespace metal;

float4 sample(const texture2d<float> t, float2 uv) {
    constexpr sampler s(filter::nearest);
    return t.sample(s, uv);
}

float4 sample(const texture2d<float> t, float2 uv, const sampler s) {
    return t.sample(s, uv);
}

struct gpuc {

    // float4 sample(texture2d, float2);

    // float4 sample(texture2d, float2, sampler);

    struct Camera {
        float4x4 mvp;
        float4x4 mvn;
    };

    struct Vertex {
        float3 position;
        float4 color;
        float4 texcoords;
    };

    struct Fragment {
        float4 position [[position]];
        float4 color;
        float4 texcoords;
    };

    struct Sample {
        float4 color;
    };

    constant const Camera& cam;

    const texture2d<float> color;

    const sampler samp;

    Sample frag(const Fragment f) const {
        Sample s;
        s.color = sample(color, f.texcoords.st, samp);
        return s;
    }

};

fragment gpuc::Sample frag(
    constant const gpuc::Camera& cam [[buffer(0)]],
    texture2d<float> color [[texture(0)]],
    sampler samp [[sampler(0)]],
    const gpuc::Fragment f [[stage_in]]
) {
    return gpuc{cam, color, samp}.frag(f);
}