1
0
Fork 0
Lua-inspired language that emits cross-platform shader code.
  • Odin 89.2%
  • Python 3.3%
  • HLSL 2.1%
  • Shell 2%
  • WGSL 1.9%
  • Other 1.5%
Find a file
Sky Johnson db71ed2c34 Emit mul() for matrix operations in HLSL backend
HLSL requires mul(matrix, vector) instead of the * operator for
matrix-vector, vector-matrix, and matrix-matrix multiplication.
The SPIR-V backend already handled this with dedicated opcodes.
Update golden tests accordingly.
2026-03-19 18:54:12 -05:00
docs Add DXBC SM5.0 binary emitter with validation tooling 2026-03-13 13:47:21 -05:00
examples Implement Phase 4: error recovery, SPIR-V debug info, SPIR-V reflection, docs 2026-03-09 15:12:24 -05:00
luma Emit mul() for matrix operations in HLSL backend 2026-03-19 18:54:12 -05:00
tests Emit mul() for matrix operations in HLSL backend 2026-03-19 18:54:12 -05:00
tools Add DXBC SM5.0 binary emitter with validation tooling 2026-03-13 13:47:21 -05:00
.gitignore Add DXBC SM5.0 binary emitter with validation tooling 2026-03-13 13:47:21 -05:00
issues.md DXBC correctness pass: add missing builtins, TGSM, indexable temps, mat*mat 2026-03-14 12:23:05 -05:00
main.odin Add DXBC SM5.0 binary emitter with validation tooling 2026-03-13 13:47:21 -05:00
plan.md Update plan with external review feedback 2026-03-08 20:52:14 -05:00
README.md Implement Phase 4: error recovery, SPIR-V debug info, SPIR-V reflection, docs 2026-03-09 15:12:24 -05:00

Luma

A Lua-inspired shader language that cross-compiles to GLSL, HLSL, MSL, WGSL, and SPIR-V. Written in pure Odin. Ships as both a standalone CLI and an embeddable library.

Quick Start

# Build
odin build . -out:luma

# Compile a shader
./luma compile shader.luma --target=glsl
./luma compile shader.luma --target=spirv -o shader.spv
./luma compile shader.luma --target=spirv --debug -o shader.spv  # with debug info

# Type-check only
./luma check shader.luma

# Inspect
./luma dump-ast shader.luma
./luma dump-ir shader.luma
./luma reflect shader.luma
./luma reflect --spirv shader.luma  # Vulkan descriptor set layout

Example

struct Uniforms
    model: mat4
    view_proj: mat4
end

@group(0) @binding(0)
uniform uniforms: Uniforms

struct VertexInput
    @location(0) position: vec3
    @location(1) uv: vec2
end

struct VertexOutput
    @builtin(position) position: vec4
    @location(0) uv: vec2
end

@entry(vertex)
function main(input: VertexInput) -> VertexOutput
    let clip_pos = uniforms.view_proj * uniforms.model * vec4(input.position, 1.0)
    return VertexOutput {
        position = clip_pos,
        uv = input.uv,
    }
end

Language Overview

Types

Category Types
Scalar bool, int, uint, float, half
Vector vec2-vec4, ivec2-ivec4, uvec2-uvec4, bvec2-bvec4
Matrix mat2-mat4, mat2x3, mat3x4, etc.
Sampler sampler2D
Array [N]T (e.g. [16]vec4)
Struct User-defined with struct ... end

Entry Points

@entry(vertex)    -- vertex shader
@entry(fragment)  -- fragment shader
@entry(compute) @workgroup_size(8, 8, 1)  -- compute shader

Bindings

@group(0) @binding(0) uniform uniforms: MyStruct   -- uniform buffer
@group(0) @binding(1) buffer data: MyBuffer         -- storage buffer
@group(1) @binding(0) uniform tex: sampler2D        -- auto-splits to texture + sampler

Groups default to 0. Bindings auto-increment within each group.

Control Flow

if condition then
    -- ...
elseif other then
    -- ...
else
    -- ...
end

for i = 0, 10 do ... end
for i = 0, 100, 2 do ... end   -- with step
while condition do ... end

Builtins

Math: abs, sign, floor, ceil, round, fract, sqrt, sin, cos, tan, pow, exp, log, min, max, clamp, mix, smoothstep, step

Vector: normalize, length, dot, cross, distance, reflect, refract

Matrix: transpose, inverse, determinant

Texture: sample(tex, uv), sample_level(tex, uv, lod)

Compute: barrier()

Attributes

Attribute Usage
@entry(stage) Mark function as shader entry point
@location(N) I/O location (auto-assigned if omitted)
@builtin(name) Built-in variable (position, vertex_index, etc.)
@group(N) Descriptor set / bind group
@binding(N) Binding number within group
@workgroup_size(X,Y,Z) Compute workgroup dimensions
@varying Struct shared between vertex output and fragment input
@spec(id) Specialization constant

Inline Entry Points

-- Tuple return syntax
@entry(fragment)
function main(@location(0) uv: vec2) -> (color: vec4)
    return vec4(uv, 0.0, 1.0)
end

-- Single output shorthand
@entry(fragment)
function main(@location(0) uv: vec2) -> vec4
    return vec4(uv, 0.0, 1.0)
end

Backend Targets

Target Flag Version
GLSL --target=glsl 4.50
HLSL --target=hlsl Shader Model 6.0
MSL --target=msl Metal 2.4
WGSL --target=wgsl WebGPU
SPIR-V --target=spirv 1.5

CLI Reference

luma compile [options] <file>    Compile shader to target
  --target=<target>              glsl, hlsl, msl, wgsl, spirv (default: glsl)
  --include-dir=<path>           Add include search path
  --debug                        Emit debug info (SPIR-V: OpLine/OpSource)
  -o <file>                      Output file (default: stdout)

luma check <file>                Parse and typecheck only
luma dump-ast <file>             Print abstract syntax tree
luma dump-ir <file>              Print intermediate representation
luma reflect [options] <file>    Emit binding reflection as JSON
  --spirv                        SPIR-V-specific reflection (descriptor sets, layout)

Library Usage

import "luma"

// Full compilation
result := luma.compile(source, luma.Compile_Options{
    target = .GLSL_450,
})

// Individual pipeline stages
ast, diags := luma.compile_parse(source, "shader.luma")
ast, sema, diags := luma.compile_check(source, "shader.luma")
ir_module, diags := luma.compile_lower(source, "shader.luma")
info, diags := luma.compile_reflect(source, "shader.luma")
spirv_info, diags := luma.compile_reflect_spirv(source, "shader.luma")

Building

Requires Odin compiler.

odin build . -out:luma

For SPIR-V validation, install spirv-tools:

spirv-val output.spv

Testing

bash tests/run_golden.sh           # run all 40 golden tests
bash tests/run_golden.sh --update  # regenerate golden files