Current status (2026-06-15): historical planning record, superseded by
docs/COMPILER_WIND_DOWN_ASSESSMENT_2026-06-15.md. Do not treat the March 2026 counts, GPU-demo claims, or SPIR-V validation bullets below as the current release gate. The current product anchor is C backend execution, HLSL/GLSL shader-source output, semantic receipts, and experimental backend preservation.
Goal: The language elite graphics programmers choose over C++ and HLSL.
Identity: Mathematically precise systems programming with algebraic effects - write your engine and your shaders in one language.
This section is preserved for roadmap context. Current backend maturity and test
counts live in STATUS.md, ARCHITECTURE.md, and the wind-down assessment.
- vec2/vec3/vec4 with constructors, arithmetic (+, -, *), field access (.x, .y, .z, .w)
- Swizzling (color.xyz, color.zyx, color.rg)
- mat4 with identity, translate, scale, perspective, multiply (mat4mat4, mat4vec4)
- Shader math: dot, cross, normalize, length, reflect, lerp, smoothstep, mix, fract, step, clamp
- ACES filmic tone mapping (verified correct output)
- Phong lighting model (verified correct output)
- Algebraic effects controlling rendering (Render effect with handler swap)
- Structs, enums, traits, generics, closures, modules, references, Option/Result, ? operator
- C backend (48 tests verified with MSVC), LLVM IR backend, WASM backend
- SPIR-V backend (3,600+ lines) with validated shader output
- A1 DONE: SPIR-V produces valid vertex + fragment shaders (pass spirv-val)
- A2 DONE: Vulkan FFI bindings, window creation
- A3 DONE: Triangle on screen - BuildLang-generated SPIR-V shaders on GPU
- A5 IN PROGRESS: Uniform buffer vertex shader (MVP matrix from UBO) - SPIR-V validates
- A5 IN PROGRESS: Textured fragment shader (texture sampling) - SPIR-V validates
- MIR types: Texture2D, Sampler, SampledImage, ShaderBinding, BindingKind
- Shader stage attributes: #[vertex], #[fragment], #[compute] in lowerer
- SPIR-V: OpTypeImage, OpTypeSampler, OpTypeSampledImage, OpImageSampleImplicitLod
- SPIR-V: Uniform buffer blocks with OpDecorate Block/Binding/DescriptorSet/ColMajor/MatrixStride
- 66K+ lines Rust compiler, 477+ unit tests
- 48 end-to-end test programs verified
- 4 validated SPIR-V shader files (triangle vert/frag + uniform vert + textured frag)
- C runtime with vector math, string, file I/O, effects handler
Goal: buildc build shader.bld --target=spirv produces a .spv file that spirv-val accepts.
Step A1.1: Validate existing SPIR-V opcodes
- The backend already has OpTypeVector, OpTypeMatrix, OpDot, OpVectorShuffle, all GLSL.std.450 builtins
- Create unit tests that construct MIR modules with vector operations and verify the SPIR-V output
- Use spirv-val (from Vulkan SDK) to validate the binary
- Fix any encoding issues (wrong opcode lengths, missing capabilities, incorrect type IDs)
Step A1.2: Shader entry points
- Implement proper vertex shader entry:
OpEntryPoint Vertex %main "main" %in_pos %out_pos - Input/output variable decoration:
OpDecorate %in_pos Location 0 - Built-in output:
OpDecorate %gl_Position BuiltIn Position - OpExecutionMode for fragment shaders (OriginUpperLeft)
Step A1.3: Minimal vertex + fragment shader pair
#[vertex]
fn vs_main(position: vec3, color: vec3) -> vec4 {
vec4(position, 1.0) // pass-through vertex shader
}
#[fragment]
fn fs_main(color: vec3) -> vec4 {
vec4(color, 1.0) // solid color fragment shader
}
Compile to two .spv files. Validate with spirv-val. This is the milestone.
Step A1.4: Wire --target=spirv into CLI
buildc build shader.bld --target=spirvproduces .spv- Multi-output: vertex and fragment shaders in separate files
- Error messages specific to shader compilation ("cannot use effect in shader function")
Deliverable: A valid SPIR-V shader pair that passes spirv-val, compiled from BuildLang source.
Goal: Call Vulkan API functions from BuildLang through C FFI.
Step A2.1: extern "C" block parsing
- Parse
extern "C" { fn vkCreateInstance(...) -> VkResult; }syntax - The parser already handles the
externkeyword (ast/item.rs has ExternBlock) - Wire it through to the lowerer - extern functions skip body lowering and emit as C
externdeclarations
Step A2.2: Vulkan type definitions
// Opaque handles
struct VkInstance { handle: u64 }
struct VkDevice { handle: u64 }
struct VkSwapchainKHR { handle: u64 }
struct VkCommandBuffer { handle: u64 }
struct VkPipeline { handle: u64 }
// Vulkan function bindings
extern "C" {
fn vkCreateInstance(info: &VkInstanceCreateInfo, alloc: u64, instance: &mut VkInstance) -> i32;
fn vkEnumeratePhysicalDevices(instance: VkInstance, count: &mut u32, devices: u64) -> i32;
// ... minimal set needed for triangle
}
Step A2.3: Minimal Vulkan bootstrap in BuildLang Write the minimum Vulkan setup code in BuildLang:
- Create instance
- Select physical device
- Create logical device
- Create swapchain
- Create render pass
- Create graphics pipeline (using the SPIR-V shaders from A1)
- Create command buffers
- Main render loop
This is ~500-800 lines of BuildLang code. It's boilerplate, but it's BuildLang boilerplate - proving the language can express Vulkan API patterns.
Step A2.4: Window creation via GLFW FFI
extern "C" {
fn glfwInit() -> i32;
fn glfwCreateWindow(width: i32, height: i32, title: &str, monitor: u64, share: u64) -> u64;
fn glfwWindowShouldClose(window: u64) -> i32;
fn glfwPollEvents();
fn glfwSwapBuffers(window: u64);
}
Deliverable: A BuildLang program that opens a window using GLFW and creates a Vulkan instance.
Goal: The "Hello World" of graphics - a colored triangle on screen, with shader and engine code both in BuildLang.
Step A3.1: Combine A1 + A2
- Compile vertex + fragment shaders to SPIR-V using
buildc --target=spirv - Load the SPIR-V into the Vulkan pipeline
- Submit draw commands
Step A3.2: The demo program
// triangle.bld - THE demo that proves BuildLang is real
#[vertex]
fn vertex_shader(position: vec3, color: vec3) -> VertexOutput {
VertexOutput {
position: vec4(position, 1.0),
color: color
}
}
#[fragment]
fn fragment_shader(input: VertexOutput) -> vec4 {
vec4(input.color, 1.0)
}
fn main() {
// Create window and Vulkan context
let window = create_window(800, 600, "BuildLang Triangle");
let renderer = create_vulkan_renderer(window);
// Load shaders (compiled from THIS file)
let pipeline = renderer.create_pipeline("vertex_shader.spv", "fragment_shader.spv");
// Define triangle vertices
let vertices = [
vec3(-0.5, -0.5, 0.0), vec3(1.0, 0.0, 0.0), // red
vec3( 0.5, -0.5, 0.0), vec3(0.0, 1.0, 0.0), // green
vec3( 0.0, 0.5, 0.0), vec3(0.0, 0.0, 1.0), // blue
];
// Render loop
while !window_should_close(window) {
poll_events();
renderer.begin_frame();
renderer.bind_pipeline(pipeline);
renderer.draw(vertices);
renderer.end_frame();
}
}
Step A3.3: Effect-controlled rendering Refactor the triangle to use effects:
effect Render {
fn begin_frame() -> (),
fn bind_pipeline(pipeline: u64) -> (),
fn draw_vertices(data: u64, count: i32) -> (),
fn end_frame() -> (),
}
// The game loop is effect-agnostic
fn game_loop(triangle: u64, pipeline: u64) ~ Render {
perform Render.begin_frame();
perform Render.bind_pipeline(pipeline);
perform Render.draw_vertices(triangle, 3);
perform Render.end_frame();
}
// The handler decides the graphics API
fn main() {
let ctx = init_vulkan();
handle {
while !should_close() {
game_loop(ctx.triangle, ctx.pipeline)
}
} with {
Render.begin_frame() => |r| { vulkan_begin_frame(ctx); r },
Render.draw_vertices(data, count) => |r| { vulkan_draw(ctx, data, count); r },
Render.end_frame() => |r| { vulkan_end_frame(ctx); r },
}
}
Deliverable: A BuildLang program that renders a colored triangle on screen, with both shader code and engine code written in BuildLang.
Goal: Apply the ACES tone mapping (already proven in test 39) as a GPU post-processing pass.
Step A4.1: Full-screen quad rendering
- Render a full-screen quad with a fragment shader
- Sample from a render target texture
Step A4.2: ACES tone mapping as a shader
#[fragment]
fn tonemap_pass(uv: vec2) -> vec4 {
let hdr_color = texture_sample(hdr_buffer, uv);
let mapped = aces_tonemap(hdr_color.xyz);
vec4(mapped, 1.0)
}
The same aces_tonemap function from test 39 - now running on the GPU.
Step A4.3: Multi-pass rendering
- Pass 1: Render scene to HDR render target
- Pass 2: Apply tone mapping
- Pass 3: Present to screen
This demonstrates BuildLang's unique value: the SAME function compiles to both CPU (for unit testing) and GPU (for real-time rendering).
Deliverable: A post-processing pipeline running on the GPU, using BuildLang shaders that were previously tested on the CPU.
Goal: Read textures and receive per-frame data from the CPU.
Step A5.1: SPIR-V texture/sampler support
- OpTypeSampler, OpTypeSampledImage, OpImageSampleImplicitLod
- Texture coordinate interpolation from vertex to fragment
Step A5.2: Uniform buffer support
#[uniform(binding = 0)]
struct CameraData {
view: mat4,
projection: mat4,
position: vec3,
}
#[vertex]
fn vs_main(position: vec3, camera: CameraData) -> vec4 {
camera.projection * camera.view * vec4(position, 1.0)
}
Step A5.3: MVP transform demo
- Rotating cube with model/view/projection matrices
- Camera controlled by uniform buffer updated from CPU each frame
Deliverable: A textured, lit, rotating cube rendered by BuildLang shaders with uniform buffer input.
Step B1.1: Fix vec3 parameter field access
- The type checker loses vector type info through function parameters
fn foo(v: vec3) -> f64 { v.x }fails becausevhas type?Tnotbuild_vec3- Fix: propagate type annotations through function parameter type resolution
Step B1.2: String != operator
- Currently
s != ""generates raw struct comparison instead of!build_string_eq() - Fix in lower_binary for Ne on BuildString types
Step B1.3: Generic enum/struct types
Option<T>instead of monomorphizedOptionI32- Requires type parameter substitution in struct/enum type definitions during lowering
Step B1.4: Iterator trait and for-in
for item in collection { ... }desugars to iterator protocol- Requires trait dispatch for Iterator::next()
Step B2.1: Vec backed by BuildVec runtime
- Push, pop, get, set, len, capacity
- For-in iteration
Step B2.2: HashMap<K,V>
- Basic hash map with string keys (simplest useful case)
- Get, set, contains, remove
Step B2.3: I/O improvements
- File read/write working end-to-end (runtime functions exist, need testing)
- stdin/stdout/stderr handles
- Buffered I/O
Step B3.1: Replace setjmp/longjmp with segmented stacks or CPS
- Current one-shot model: only first perform in a handle block fires
- Multi-shot: generators, async streams, backtracking search all become possible
- Approach: CPS transform in the MIR → C backend, or embed libmprompt
Step B3.2: Async/await as effect sugar
// async/await is just syntax sugar for the Async effect
async fn fetch_data(url: str) -> str {
// desugars to: perform Async.yield_() between IO operations
}
Step B3.3: Effect polymorphism in codegen
fn map<A, B, E>(items: Vec<A>, f: fn(A) ~ E -> B) ~ E -> Vec<B>
- The type system already supports open effect rows
- Codegen needs to handle effect row variables
Step B4.1: Wire package manager to CLI
buildc pkg init- create Build.tomlbuildc pkg add json- add dependencybuildc pkg build- build with dependencies
Step B4.2: Module system improvements
- Nested modules:
mod rendering::pipeline - pub/private visibility
- use glob imports:
use math::*
Step B4.3: LSP completion
- Wire the LSP server properly
- VS Code extension with syntax highlighting
- Go-to-definition, autocomplete, inline errors
Step B5.1: Source location in errors
- "error at shader.bld:15:8: undefined variable 'texCoord'"
- Underline the exact span in the source
Step B5.2: Suggestion engine
- "did you mean 'vec3'?" for typos
- "function requires effect ~IO, add it to the signature" for unhandled effects
Step B5.3: Warning system
- Unused variables, unreachable code
- Performance warnings for shader code (branching in fragment shaders, etc.)
Goal: One program. One file. Opens a window. Renders a spinning, lit, textured cube. Shader and engine code both in BuildLang. Effects control the rendering pipeline.
// cube_demo.bld - The BuildLang Graphics Showcase
//
// This single file contains:
// - Vertex and fragment shaders (compile to SPIR-V for GPU)
// - Phong lighting with specular highlights
// - ACES tone mapping post-process
// - Matrix transforms (model/view/projection)
// - Effect-controlled rendering (swap Vulkan/mock/profiler)
// - All in one language, all type-checked together
#[vertex]
fn vs_main(pos: vec3, normal: vec3, uv: vec2, mvp: mat4) -> VertexOutput {
VertexOutput {
position: mvp * vec4(pos, 1.0),
world_normal: normal,
tex_coord: uv,
}
}
#[fragment]
fn fs_main(input: VertexOutput) -> vec4 {
let light = normalize(vec3(1.0, 1.0, 0.5));
let color = phong_lighting(input.world_normal, light, vec3(0.0, 0.0, 1.0), 64.0);
let mapped = aces_tonemap(color);
vec4(mapped, 1.0)
}
effect Render {
fn clear(color: vec4) -> (),
fn draw_mesh(mesh: u64, transform: mat4) -> (),
fn present() -> (),
}
fn main() {
let window = create_window(1280, 720, "BuildLang Cube");
let ctx = init_vulkan(window);
let cube = load_cube_mesh(ctx);
let mut angle: f64 = 0.0;
handle {
while !should_close(window) {
angle = angle + 0.01;
let model = mat4_rotate_y(angle);
let view = mat4_look_at(vec3(0.0, 2.0, 5.0), vec3(0.0, 0.0, 0.0), vec3(0.0, 1.0, 0.0));
let proj = mat4_perspective(0.785, 1.78, 0.1, 100.0);
let mvp = proj * view * model;
perform Render.clear(vec4(0.1, 0.1, 0.15, 1.0));
perform Render.draw_mesh(cube, mvp);
perform Render.present();
poll_events(window);
}
} with {
Render.clear(c) => |r| { vk_clear(ctx, c); r },
Render.draw_mesh(m, t) => |r| { vk_draw(ctx, m, t); r },
Render.present() => |r| { vk_present(ctx); r },
}
}
This is the demo that changes minds. One file. Shader + engine. CPU-testable lighting. GPU-accelerated rendering. Effect-swappable backend.
Goal: Build a BuildLang version of ReShade's post-processing framework - the kind of tool Pascal Gilcher works with daily.
- Multiple configurable post-processing effects
- Each effect is a BuildLang module with a fragment shader
- Effects chain: scene → bloom → tone map → color grade → FXAA → output
- UI for enabling/disabling effects (using effects for UI state!)
- Hot reload: change a shader file, see it update immediately
Goal: A small but complete game engine written entirely in BuildLang.
- Scene graph with entities, components, transforms
- Forward rendering with point/directional lights
- Material system with shader permutations
- Asset loading (OBJ meshes, PNG textures via FFI)
- Input handling (keyboard/mouse via GLFW FFI)
- Physics (simple AABB collision)
- Audio (via FFI to miniaudio or similar)
Everything uses effects:
effect Physics { fn step(dt: f64) -> (), fn raycast(origin: vec3, dir: vec3) -> Hit }
effect Audio { fn play(sound: str) -> (), fn set_volume(vol: f64) -> () }
effect Input { fn is_key_pressed(key: i32) -> bool, fn mouse_pos() -> vec2 }
Swap handlers for testing. Record effects for replay. Profile by timing handlers.
| Phase | Weeks | Milestone |
|---|---|---|
| A1 | 1-3 | SPIR-V backend produces valid shader pairs |
| A2 | 3-5 | Vulkan FFI bindings, window creation |
| A3 | 5-7 | Triangle on screen - both shader and engine in BuildLang |
| A4 | 7-9 | Post-processing (ACES tone mapping on GPU) |
| A5 | 9-11 | Textures, uniform buffers, MVP transforms |
| B1 | 11-13 | Type system fixes, generic types |
| B2 | 13-15 | Vec, HashMap, I/O stdlib |
| B3 | 15-18 | Multi-shot continuations, async/await |
| B4 | 18-20 | Package manager, module improvements, LSP |
| B5 | 20-22 | Error messages, warnings, diagnostics |
| C1 | 22-24 | Spinning cube demo - the showcase |
| C2 | 24-28 | ReShade-style post-processing framework |
| C3 | 28-36 | Minimal game engine |
For Pascal Gilcher: "I can write my ReShade shaders in BuildLang, test them on CPU, compile to SPIR-V, and hot reload them. The swizzling and math feel native. Effects let me swap between quality presets without recompiling."
For Boris Vorontsov: "I can build ENB-level post-processing in BuildLang with zero-overhead abstractions. The memory layout control is precise. The SPIR-V output is optimal. I can inject effects into any game's rendering pipeline."
For every game programmer: "I write my engine and my shaders in the same language. I test rendering on CPU. I debug shader math with printf. The effect system replaces my dependency injection framework, my event system, and my command pattern - all at once."
BuildLang: Mathematically precise. GPU-native. Effect-controlled. One language for everything.