Skip to content

manuel5975p/simple_wgsl

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

99 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Simple WGSL

A feature-complete shader compiler library written in pure C99. Converts between WGSL, GLSL 450, MSL, PTX, HLSL, and SPIR-V through a shared intermediate representation.

WGSL ──┐              ┌── SPIR-V
GLSL ──┤              ├── WGSL
MSL  ──┼── [SSIR] ────┼── GLSL 450
PTX  ──┤              ├── MSL
SPIR-V ┘              ├── HLSL
                      └── V3D (Pi 5)

Why Simple WGSL?

  • Single header, zero dependencies -- include simple_wgsl.h and link the static library. No runtime dependencies beyond the C standard library.
  • Eight languages, one pipeline -- parse WGSL, GLSL, MSL, or PTX source; ingest SPIR-V binaries; emit to any of six output formats. Every conversion flows through the same intermediate representation.
  • ~46k lines of C99 -- no templates, no inheritance hierarchies, no build system complexity. Every compilation unit is a single .c file. Builds in under a second.
  • Embeddable -- all memory allocation goes through overridable macros (NODE_MALLOC, SSIR_MALLOC, etc.), so you can plug in your own allocator for game engines, embedded systems, or WASM targets.
  • Fuzz-hardened -- eight libFuzzer targets with AddressSanitizer + UndefinedBehaviorSanitizer have been run continuously.

Quick Start

#include "simple_wgsl.h"
#include <stdio.h>

int main(void) {
    // Parse WGSL source
    const char *src =
        "@vertex fn main(@builtin(vertex_index) idx: u32) -> @builtin(position) vec4f {\n"
        "    return vec4f(0.0, 0.0, 0.0, 1.0);\n"
        "}\n";

    WgslAstNode *ast = wgsl_parse(src);
    WgslResolver *resolver = wgsl_resolver_build(ast);

    // Compile to SPIR-V
    WgslLowerOptions opts = {0};
    opts.env = WGSL_LOWER_ENV_VULKAN_1_2;
    opts.enable_debug_names = 1;

    uint32_t *spirv;
    size_t word_count;
    WgslLowerResult res = wgsl_lower_emit_spirv(ast, resolver, &opts, &spirv, &word_count);

    if (res == WGSL_LOWER_OK)
        printf("Generated %zu SPIR-V words\n", word_count);

    wgsl_lower_free(spirv);
    wgsl_resolver_free(resolver);
    wgsl_free_ast(ast);
}

Building

Compile directly without a build system (requires SPIR-V Headers installed):

cc -c *.c -I/path/to/SPIRV-Headers/include
ar rcs libsimple_wgsl.a *.o

Or use CMake (3.16+), which fetches SPIR-V Headers automatically if not found on the system:

cmake -B build -G Ninja
ninja -C build

Run the tests (requires Google Test, fetched automatically):

cmake -B build -G Ninja -DWGSL_BUILD_TESTS=ON
ninja -C build
ctest --test-dir build --output-on-failure

Build options (All testing/debugging related):

Option Default Description
WGSL_BUILD_TESTS ON Build Google Test suite
WGSL_BUILD_EXPRESSION_TESTS OFF Build golden-file expression tests (needs expressions/ data)
WGSL_BUILD_FUZZ OFF Build libFuzzer fuzz targets
WGSL_BUILD_EGL_TESTS OFF Build EGL/OpenGL compute validation tests
WGSL_BUILD_METAL_EXAMPLES OFF Build Metal/MSL examples (macOS only)
WGSL_USE_ASAN OFF Enable AddressSanitizer
WGSL_USE_UBSAN OFF Enable UndefinedBehaviorSanitizer
WGSL_USE_LSAN OFF Enable LeakSanitizer (standalone, not with ASan)
WGSL_COVERAGE OFF Enable code coverage with llvm-cov (requires Clang)

Architecture

graph LR
    WGSL["WGSL Source"] --> P1[wgsl_parse]
    GLSL["GLSL 450 Source"] --> P2[glsl_parse]
    MSL["MSL Source"] --> P3[msl_to_ssir]
    PTX["PTX Assembly"] --> P4[ptx_to_ssir]
    P1 --> AST[AST]
    P2 --> AST
    AST --> R[Resolver]
    R --> L[Lowering]
    L --> SSIR["SSIR Module"]
    L --> SPIRV["SPIR-V Binary"]
    SPIRV --> D[spirv_to_ssir]
    P3 --> SSIR
    P4 --> SSIR
    D --> SSIR
    SSIR --> E1[ssir_to_spirv]
    SSIR --> E2[ssir_to_wgsl]
    SSIR --> E3[ssir_to_glsl]
    SSIR --> E4[ssir_to_msl]
    SSIR --> E5[ssir_to_hlsl]
    SSIR --> E6[ssir_to_v3d]
    E1 --> SPIRV2["SPIR-V"]
    E2 --> WGSL2["WGSL"]
    E3 --> GLSL2["GLSL 450"]
    E4 --> MSL2["MSL"]
    E5 --> HLSL2["HLSL"]
    E6 --> V3D2["V3D QPU"]
Loading

SSIR (Simple Shader Intermediate Representation) sits at the center. All source languages parse into it, all output formats emit from it. See TECHNICAL.md for the full architecture deep-dive.

Source Files

File Lines Purpose
simple_wgsl.h 2024 Unified public API header
wgsl_parser.c 2432 WGSL lexer + recursive-descent parser
glsl_parser.c 2635 GLSL 450 lexer + recursive-descent parser
msl_parser.c 2405 MSL lexer + parser (produces SSIR directly)
ptx_parser.c 1879 PTX assembly lexer + recursive-descent parser
ptx_lower.c 3099 PTX to SSIR lowering (register tracking, BDA support, texture/surface ops)
wgsl_resolve.c 1266 Semantic analysis and symbol resolution
wgsl_lower.c 8103 AST to SSIR to SPIR-V compilation
wgsl_raise.c 2148 SPIR-V to WGSL decompilation
ssir.c 3430 SSIR module, type system, and builder API
ssir_to_spirv.c 3056 SSIR to SPIR-V serialization
ssir_to_wgsl.c 1603 SSIR to WGSL text emission
ssir_to_glsl.c 1684 SSIR to GLSL 450 text emission
ssir_to_msl.c 2068 SSIR to Metal Shading Language emission
ssir_to_hlsl.c 1470 SSIR to HLSL emission
spirv_to_ssir.c 2566 SPIR-V to SSIR deserialization
ssir_to_v3d.c 2603 SSIR to V3D QPU binary emission (Raspberry Pi 5)

Extensions

simple_wgsl supports opt-in language extensions via enable directives:

Extension What it enables
immediate_address_space var<immediate> for push constants with scalar, vector, matrix, and struct types
immediate_arrays Additionally allows arrays in var<immediate> (implies immediate_address_space)

See the TUTORIAL.md immediates section for usage details and the TECHNICAL.md for the full specification.

Documentation

  • TECHNICAL.md -- Architecture, SSIR specification, full API reference, type system, instruction set, and internal design details.
  • TUTORIAL.md -- Step-by-step guides for every major use case: parsing, compiling, decompiling, cross-compiling, immediates (push constants), building the SSIR programmatically, custom allocators, and more.

CUDA-on-Vulkan Runtime (cuvk_runtime)

When the Vulkan SDK is available, the build also produces cuvk_runtime -- a drop-in libcuda.so.1 replacement that runs CUDA compute kernels on any Vulkan GPU. Cross-platform: Linux, macOS (MoltenVK / Kosmickrisp), Windows. CUDA binaries compiled with nvcc link against this library at runtime (via LD_PRELOAD or direct linking), and their PTX kernels are cross-compiled to SPIR-V on the fly.

CUDA fatbin --> extract PTX (zstd/lz4) --> ptx_to_ssir --> ssir_to_spirv --> VkComputePipeline

Key features:

  • BDA mode (Vulkan 1.1+ with buffer device address): kernel parameters packed into push constants, pointers as 64-bit device addresses
  • Descriptor mode (fallback): each pointer parameter becomes a storage buffer descriptor binding
  • Stream-based dispatch: kernel launches and event timestamps are recorded into per-stream command buffers for correct ordering and accurate GPU-side timing
  • No libvulkan link dependency: all Vulkan functions loaded at runtime via X-macro function pointer table (dlsym/LoadLibraryA bootstrap)

Includes stub implementations for libcudart.so.1, libcublas.so.13, and libcufft.so.12. The cuFFT stub implements Cooley-Tukey and Stockham FFT via WGSL compute shaders. See cuvk_runtime/ for source (~12.7k lines).

Dependencies

Dependency Version Source
SPIR-V Headers (Khronos) vulkan-sdk-1.4.341 System or FetchContent
Google Test v1.14.0 FetchContent (tests only)
Vulkan SDK system Optional, for GPU tests and cuvk_runtime
zstd v1.5.7 FetchContent (cuvk_runtime only)
spirv-val / spirv-dis system Optional, SPIRV-Tools for test validation

License

See LICENSE.

About

A fast and lightweight compiler for the WebGPU Shading Language WGSL

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors