Skip to content

shader

stable

WGSL and GLSL shader source utilities: brace validation, entry-point and uniform extraction, comment stripping, header injection, and code templates.

use plugin shader::{validate_braces, extract_entry_points, extract_uniforms, …}
11 functions Graphics
/ filter jk navigate Esc clear
Functions (11)
  1. validate_braces Check that braces are balanced
  2. extract_entry_points List shader entry-point function names
  3. extract_uniforms List uniform variable declarations
  4. strip_comments Remove line and block comments
  5. add_header Prepend #define directives to source
  6. wgsl_vertex_template Generate a WGSL vertex shader template
  7. wgsl_fragment_template Generate a WGSL fragment shader template
  8. wgsl_compute_template Generate a WGSL compute shader template
  9. detect_shader_language Detect whether source is WGSL or GLSL
  10. count_lines Count total, code, blank, and comment lines
  11. validate_source Run combined validation checks

Overview

shader is a dependency-free toolkit for inspecting and generating shader source code as plain strings — there are no GPU handles, no compilation, and no state to manage. Every function takes a source string (or a name) and returns a string, bool, or plain table, so it slots cleanly into a build step, a hot-reload watcher, or a code generator. It understands both WGSL (the modern @vertex / var<uniform> dialect) and GLSL (void main, uniform Type name;), and most functions handle either language transparently.

Reach for it when you need to sanity-check hand-written or generated shaders (balanced braces and parentheses, a present entry point), introspect a shader's entry points and uniforms, normalize source by stripping comments or injecting #define headers, or scaffold WGSL boilerplate for vertex, fragment, and compute stages.

Common patterns

Validate a shader before handing it to the GPU, and bail out early on errors:

use plugin shader::{validate_source}

let src = "@vertex fn vs() -> @builtin(position) vec4<f32> { return vec4(0.0); }"
let report = validate_source(src)
if report["valid"] {
  print("shader ok")
} else {
  print("invalid shader: {report["errors"][1]}")
}

Generate a vertex/fragment pair, confirm the dialect, and tally its size:

use plugin shader::{wgsl_vertex_template, detect_shader_language, count_lines}

let vs = wgsl_vertex_template("vs_main")
print("language: {detect_shader_language(vs)}")
let stats = count_lines(vs)
print("{stats["code"]} code lines, {stats["blank"]} blank")

Introspect a shader's interface — its entry points and the uniforms it expects:

use plugin shader::{extract_entry_points, extract_uniforms}

let src = "
@group(0) @binding(0) var<uniform> mvp: mat4x4<f32>;
@vertex
fn vs_main(in: VertexInput) -> VertexOutput { }
"
print("entry: {extract_entry_points(src)[0]}")
let u = extract_uniforms(src)
print("uniform {u[0]["name"]}: {u[0]["type"]}")

Check that braces are balanced

Returns true if all curly braces in the shader source are balanced. Correctly skips braces inside // line comments and /* */ block comments.

use plugin shader::{validate_braces}

let src = "@vertex fn vs() { return; }"
print(validate_braces(src))  // true

let broken = "@vertex fn vs() { return;"
print(validate_braces(broken))  // false

List shader entry-point function names

Scans WGSL source for @vertex, @fragment, and @compute attributes and returns a table of the associated function names. Also recognizes void main() in GLSL source.

use plugin shader::{extract_entry_points}

let src = "
@vertex
fn vs_main(in: VertexInput) -> VertexOutput { }

@fragment
fn fs_main() -> @location(0) vec4<f32> { }
"
let eps = extract_entry_points(src)
print(eps[0])  // vs_main
print(eps[1])  // fs_main

List uniform variable declarations

Extracts uniform variable declarations from WGSL (var<uniform>) and GLSL (uniform Type name;). Each entry is a table with name and type fields.

use plugin shader::{extract_uniforms}

let src = "
@group(0) @binding(0) var<uniform> mvp: mat4x4<f32>;
@group(0) @binding(1) var<uniform> time: f32;
"
let uniforms = extract_uniforms(src)
print(uniforms[0]["name"])  // mvp
print(uniforms[0]["type"])  // mat4x4<f32>
print(uniforms[1]["name"])  // time

It works on GLSL declarations too (uniform Type name;):

use plugin shader::{extract_uniforms}

let glsl = "uniform mat4 u_mvp;\nuniform float u_time;"
let u = extract_uniforms(glsl)
print("{u[0]["type"]} {u[0]["name"]}")  // mat4 u_mvp

Remove line and block comments

Removes all // line comments and /* */ block comments from shader source, preserving newlines so line numbers remain meaningful.

use plugin shader::{strip_comments}

let src = "// vertex shader\nfn vs() { /* init */ return; }"
let clean = strip_comments(src)
print(clean)
// "\nfn vs() {  return; }"

Prepend #define directives to source

Prepends #define KEY VALUE lines from the defines table to the shader source. Useful for injecting compile-time constants into GLSL shaders.

use plugin shader::{add_header}

let src = "void main() { float x = MAX_LIGHTS; }"
let result = add_header(src, #{"MAX_LIGHTS": "8", "SHADOW_PASS": "1"})
print(result)
// #define MAX_LIGHTS 8
// #define SHADOW_PASS 1
// void main() { float x = MAX_LIGHTS; }

Numeric values are accepted as well as strings, so a config map of feature flags can be turned into a header in one call:

use plugin shader::{add_header, detect_shader_language}

let base = "void main() { gl_Position = vec4(0); }"
let withDefs = add_header(base, #{"QUALITY": 2, "USE_FOG": "1"})
print(detect_shader_language(withDefs))  // glsl

Generate a WGSL vertex shader template

Generates a complete WGSL vertex shader boilerplate with standard VertexInput/VertexOutput structs, an MVP uniform, and a vertex function named name.

use plugin shader::{wgsl_vertex_template}

let src = wgsl_vertex_template("vs_main")
print(src)
// struct VertexInput { @location(0) position: vec3<f32>, ... }
// @vertex fn vs_main(in: VertexInput) -> VertexOutput { ... }

Generate a WGSL fragment shader template

Generates a WGSL fragment shader boilerplate with diffuse texture sampling, a sampler, and basic Lambertian lighting in a function named name.

use plugin shader::{wgsl_fragment_template}

let src = wgsl_fragment_template("fs_main")
print(detect_shader_language(src))  // wgsl

Generate a WGSL compute shader template

Generates a WGSL compute shader boilerplate with a storage buffer binding and a workgroup size annotation. workgroup_x/y/z default to 64, 1, 1 if not provided.

use plugin shader::{wgsl_compute_template}

let src = wgsl_compute_template("cs_main", 256, 1, 1)
print(src)
// @compute @workgroup_size(256, 1, 1)
// fn cs_main(@builtin(global_invocation_id) id: vec3<u32>) { ... }

Detect whether source is WGSL or GLSL

Heuristically detects the shader language by scoring WGSL markers (@vertex, var<, fn , etc.) against GLSL markers (#version, void main, gl_Position, etc.). Returns "wgsl", "glsl", or "unknown".

use plugin shader::{wgsl_vertex_template, detect_shader_language}

let src = wgsl_vertex_template("vs")
print(detect_shader_language(src))  // wgsl

let glsl = "#version 330 core\nvoid main() { gl_Position = vec4(0); }"
print(detect_shader_language(glsl))  // glsl

Source with no recognizable markers falls through to "unknown", which is handy as a guard before picking a compiler path:

use plugin shader::{detect_shader_language}

let lang = detect_shader_language("just a plain text blob")
if lang == "unknown" {
  print("cannot tell which shader dialect this is")
}

Count total, code, blank, and comment lines

Counts lines in shader source and categorises them. Returns a table with total, code, blank, and comment counts.

use plugin shader::{wgsl_vertex_template, count_lines}

let src = wgsl_vertex_template("vs_main")
let stats = count_lines(src)
print(stats["total"])    // number of total lines
print(stats["code"])     // non-blank, non-comment lines
print(stats["blank"])    // empty lines
print(stats["comment"])  // lines starting with // or /*

Run combined validation checks

Runs combined checks on a shader source: balanced braces, balanced parentheses, and the presence of at least one entry point (@vertex, @fragment, @compute, or void main). Returns a table with valid (bool) and errors (table of error strings).

use plugin shader::{validate_source}

let good = "@vertex fn vs() -> @builtin(position) vec4<f32> { return vec4(0.0); }"
let result = validate_source(good)
print(result["valid"])  // true

let bad = "fn broken( { }"
let r2 = validate_source(bad)
print(r2["valid"])     // false
print(r2["errors"][1]) // unbalanced parentheses (or similar)

Because errors is itself a table, you can iterate every problem found in a single pass instead of inspecting one index at a time:

use plugin shader::{validate_source}

let r = validate_source("fn broken( {")
for err in r["errors"] {
  print("- {err}")
}
enespt-br