javelin

Graphics programming system with JIT capabilities and more

Stars
2

Javelin

Javelin is a system for graphics programming; to this serve this purpose, it aims to provide the following:

  • A just-in-time (JIT) compilation framework for shader and kernel programming
  • Common graphics constructs for fast prototyping of visual applications and research
  • A programming language for end-to-end graphics applications, shaders included
  • An editor for managing and viewing 3D scenes

Many components of Javelin are a work in progress. See the road map to get an idea of what is coming next.

Intermediate Representation Emitter

The Intermediate Representation Emitter (IRE) is Javelin's JIT framework for runtime shader and kernel generation. It aims to provide a seamless C++ experience when working with graphics shaders as well as multi-platform kernels.

For example, the following C++ code demonstrates how to use the IRE to write a simple vertex-fragment shader that simply projects vertices and sets the surface color to a static value.

// Model-view-projection structure
struct MVP {
    // Typical matrix structures
    mat4 model;
    mat4 view;
    mat4 proj;

    // Define a method to perform the projection
    vec4 project(vec3 position) {
        return proj * (view * (model * vec4(position, 1.0)));
    }

    // Define the layout structure of this struct for generated shader code
    auto layout() const {
        return uniform_layout(
            "MVP",
            named_field(model),
            named_field(view),
            named_field(proj)
        );
    }
};

// Shader kernels
void vertex()
{
    // Vertex inputs
    layout_in <vec3> position(0);

    // Projection information
    push_constant <MVP> mvp;

    // Projecting the vertex
    gl_Position = mvp.project(position);
}

void fragment(float3 color)
{
    // Resulting fragment color
    layout_out <vec4> fragment(0);

    // Set the color
    fragment = vec4(color.x, color.y, color.z, 1);
}

The corresponding GLSL source code can be generated as follows:

auto vs_callable = procedure("main") // Name of the kernel
	<< vertex;

// Instantiate a fragment shader that colors the surface RED
auto fs_callable = procedure("main")
	<< std::make_tuple(float3(1, 0, 0)) // Pass arguments to the fragment function
	<< fragment;

std::string vertex_shader = link(vs_callable).generate_glsl();
std::string fragment_shader = link(fs_callable).generate_glsl();

Other, more complex examples can be found in the examples directory.

The IRE supports compiling to multiple targets, with more to come in the future:

  • GLSL, Vulkan-style (working) and OpenGL-style (planned)
  • C++, generates C++11 code (working), with OpenMP support (planned)
  • Host exectuable code through JIT, with libgccjit (working) and LLVM (planned) backends
  • SPIRV assembly (planned) and binary (planned)
  • CUDA kernels through NVRTX (planned)

All IRE features are provided in the include/ire/core.hpp header file. The corresponding library is built into javelin-ire using CMake. See the Building section for more details.

Asides:

Why bother with JIT shader compilation?

Building

This project is configured with CMake and requires C++20. Additional dependencies are listed below:

  • libgccjit (14.x.x onwards) for the IRE to load compiled kernels
  • GLFW3 for graphics applications
  • Vulkan, SPIRV, and glslang for Vulkan specific graphics applications

The following should be sufficient to get started:

$ cmake -B build .
$ cmake --build build -j
OR
$ cmake --build build -j -t [target] # if only certains targets are needed