在纯 Go 中构建 Shader 编译器:naga 达到 v0.4.0

发布: (2025年12月12日 GMT+8 08:58)
6 min read
原文: Dev.to

Source: Dev.to

纯 Go 构建着色器编译器:naga 达到 v0.4.0 的封面图

TL;DR

naga 将 WGSL(WebGPU 着色语言)编译为 SPIR‑V 字节码。

  • 17 000 行纯 Go。
  • 无 CGO。无外部依赖。只需 go build

v0.4.0 现已支持带原子操作和屏障的计算着色器

  • 完整的类型推断系统
  • 203 条测试,约 61 % 覆盖率
  • 可用于图形和计算工作负载的生产就绪

为什么要构建着色器编译器?

如果你在 Go 中进行 GPU 编程,你一定遇到过这样的障碍:着色器编译总是需要外部工具(Rust 的 naga、Google 的 glslc、NVIDIA 的工具链)。这意味着:

  • 额外的构建依赖
  • 平台特定的二进制文件
  • CGO 或子进程调用
  • 部署复杂

GoGPU 生态的目标是消除这些摩擦。一个纯 Go 的着色器编译器意味着:

go build ./...
# 就这么简单。无需 cmake、Rust 工具链、DLL。

发展历程:v0.1.0 → v0.4.0

v0.1.0 — 基础(约 10 K 行代码)

  • WGSL 词法分析器,识别 140+ 记号
  • 递归下降解析器
  • 中间表示(33 种表达式,16 种语句)
  • SPIR‑V 二进制写入器,包含 100+ 操作码

结果:顶点和片段着色器能够成功编译。

v0.2.0 — 类型系统(约 2 K 行代码)

难点:类型推断。SPIR‑V 要求对所有内容显式标注类型,而 WGSL 允许推断:

let x = 1.0;           // f32
let v = vec3(1.0);     // vec3
let n = normalize(v); // vec3,依据函数返回值推断

实现了完整的类型解析引擎,能够在每个表达式中追踪类型。

v0.3.0 — 纹理(约 3 K 行代码)

加入纹理操作——图形编程的核心:

@fragment
fn main(@location(0) uv: vec2) -> @location(0) vec4 {
    return textureSample(myTexture, mySampler, uv);
}

实现了 SPIR‑V 图像操作,如 OpSampledImageOpImageSampleImplicitLod 等。

v0.4.0 — 计算着色器(约 2 K 行代码)

最新发布带来了 GPU 计算能力:

@group(0) @binding(0)
var counter: atomic;

@compute @workgroup_size(64)
fn main(@builtin(global_invocation_id) id: vec3) {
    atomicAdd(&counter, 1u);
    workgroupBarrier();
}

关键新增

  • 存储缓冲区访问模式readread_write
  • 工作组共享内存var
  • 9 种原子操作:add、sub、min、max、and、or、xor、exchange、compare‑exchange
  • 3 种屏障类型:workgroup、storage、texture
  • 取地址运算符& 用于原子指针

架构

┌─────────────────────────────────────────────────────┐
│                   WGSL 源码                        │
└─────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│              词法分析器(140+ 记号)                │
│         wgsl/lexer.go — ~400 行代码                 │
└─────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│         解析器(递归下降)                         │
│        wgsl/parser.go — ~1400 行代码               │
└─────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│              AST → IR 降级                         │
│         wgsl/lower.go — ~1100 行代码               │
└─────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│          中间表示(IR)                            │
│    33 种表达式类型,16 种语句类型                  │
│     类型推断 + 去重                                 │
└─────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│             SPIR‑V 后端                             │
│       spirv/backend.go — ~1800 行代码              │
│       100+ 操作码,GLSL.std.450                     │
└─────────────────────────────────────────────────────┘


┌─────────────────────────────────────────────────────┐
│              SPIR‑V 二进制                         │
│         兼容 Vulkan 的字节码                       │
└─────────────────────────────────────────────────────┘

使用方式

作为库

import "github.com/gogpu/naga"

func main() {
    source := `
@vertex
fn main(@builtin(vertex_index) idx: u32) -> @builtin(position) vec4 {
    return vec4(0.0, 0.0, 0.0, 1.0);
}
`
    spirv, err := naga.Compile(source)
    if err != nil {
        log.Fatal(err)
    }
    // spirv 已可用于 Vulkan
}

命令行工具

go install github.com/gogpu/naga/cmd/nagac@latest

nagac shader.wgsl -o shader.spv
nagac -debug shader.wgsl -o shader.spv  # 带调试名称

带警告的编译

v0.4.0 引入了未使用变量检测:

result, err := naga.LowerWithWarnings(ast)
for _, w := range result.Warnings {
    fmt.Printf("Warning: %s at line %d\n", w.Message, w.Span.Line)
}

以下划线 _ 开头的变量会被有意忽略(类似 Go 的风格)。

支持的特性

类别特性
类型f32f64i32u32boolvec2‑4mat2x2‑4x4、数组、结构体、原子类型
纹理texture_2dtexture_3dtexture_cubesampler
着色器@vertex@fragment@compute
绑定@location@group/@binding@builtin
存储uniformstorage(read/read_write)、workgroup
函数50+ 内建函数(数学、几何、插值、原子、屏障)
控制流if/elseforwhileloopbreakcontinue

下一步计划

v0.5.0

  • GLSL 后端 – 输出 GLSL 以兼容 OpenGL
  • 源码映射 – 调试信息将 SPIR‑V 映射回 WGSL
  • 优化 passes – 常量折叠、死代码消除

v1.0.0

  • 完整符合 WGSL 规范
  • HLSL/MSL 后端,支持 DirectX/Metal
  • 生产级硬化

性能

编译速度很快。普通着色器的编译时间不足 5 ms。完整测试套件(203 条)运行约 2 秒

尚未对 Rust 的 naga 做基准对比,但我们的目标不是追求原始速度,而是提供一个 纯 Go 的解决方案,能够无缝集成到 Go 项目中。

Back to Blog

相关文章

阅读更多 »