声明式WebGPU与S表达式

Lobsters Hottest 工具

摘要

Pngine是一个用于WebGPU的声明式格式与运行时,它使用S表达式来简化WebGPU配置的声明与验证,支持跨平台共享,并能导出为HTML或嵌入运行时的PNG等格式。

<p><a href="https://lobste.rs/s/hypo37/declarative_webgpu_with_s_expressions">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/08/23 21:23

# 基于 S 表达式的声明式 WebGPU 实现 来源:https://hugodaniel.com/posts/declarative-webgpu-with-s-expressions/ 可部署与共享的 WebGPU 接线方案 2026年8月23日 关于作者 (https://hugodaniel.com/) 新手入门 (https://hugodaniel.com/start-here/) 所有文章 (https://hugodaniel.com/posts) 订阅源 (https://hugodaniel.com/atom.xml) 这是我过去两年半开发的 WebGPU 声明式格式与运行时——**pngine**。以下代码展示了如何用 S 表达式编写与 WebGPU 规范 (https://www.w3.org/TR/webgpu/) 1:1 对应的接线配置: ```lisp (shader-module :name code :code """ @vertex fn vertexMain( @builtin(vertex_index) VertexIndex : u32 ) -> @builtin(position) vec4f { var pos = array( vec2(0.0, 0.5), vec2(-0.5, -0.5), vec2(0.5, -0.5) ); return vec4f(pos[VertexIndex], 0.0, 1.0); } @fragment fn fragMain() -> @location(0) vec4f { return vec4(1.0, 0.0, 0.0, 1.0); } """) (render-pipeline :name pipeline :layout auto (vertex :module code :entry vertexMain) (fragment :module code :entry fragMain (target :format preferred-canvas-format)) ) (render-pass :name trianglePass (color-attachment :view context-current-texture :clear-value [0 0 0 0] :load-op clear :store-op store) :pipeline pipeline (draw :vertex-count 3)) (frame :name main :perform [trianglePass]) ``` 这段代码通过 S 表达式构建了简单的红色三角形,实现了与 WebGPU 规范完全对应的声明式接线。 ## 1-2-3 Pngine 的核心能力 Pngine 具备三大核心功能: - 允许以跨平台方式声明并部署 WebGPU 接线方案(包含着色器与 CPU/WASM 初始化),不仅限于浏览器环境,也支持 rust wgpu,并已适配 Android/iOS 播放器 - 通过 WGSL 反射和规范检查验证声明的 WebGPU 配置,无需实时上下文即可生成错误与警告提示,并提供 LSP 支持实现实时校验 - 可将所有内容导出为单个 .html 文件、.zip 包或 .png 文件,支持将可执行的 PNG 文件直接交付给他人(PNG 本身不自动执行,仅包含字节码,需配合微型播放器运行) 最后一点正是 **pngine** 名称的由来——S 表达式会被编译为二进制表示,由微型运行时解释执行。运行时与负载数据可嵌入 PNG 附加数据块,而 PNG 文件本身仍作为预览图像呈现。 完整说明请访问 [专属页面](https://pngine.com/)。本项目采用 CC0 协议,代码托管于 [GitHub](https://github.com/HugoDaniel/pngine) 用于问题追踪与讨论,发布版本源自作者自建仓库。 ## 为何需要这样的工具? 共享定制化的 WebGPU 接线方案长期存在技术门槛。我需要单个文件能同时描述 WebGPU 管线/缓冲区/通道等结构,以及着色器模块与 WGSL 代码。虽然现有通用编程语言都能实现,但我追求更高层的抽象——一种可作为其他工具基础、能深度解析(供人或机器使用)且无干扰的解决方案。 [SJON](https://hugodaniel.com/pages/sjon) 为我提供了构建 DSL 的途径,可通过 S 表达式实现自动验证与一致性交付。此方案专注于 WebGPU 接线部分,着色器代码仍保持原生 WGSL 格式。 程序的可读数据表征是 pngine 的核心理念,这种特性使得程序结构本身成为常规结构化数据,从而支持预先验证、回放、检查、压缩、序列化与机器生成——所有操作都作用于同一数据表示。 ## 打包你的接线方案 分享 WebGPU 管线需要预先约定大量规范:使用 TypeScript/JavaScript/Rust 哪种语言?句柄如何表示与传递?管线用于动画、游戏还是其他场景?这些选择直接影响接线方案的打包与共享方式。 Pngine 旨在最小化这些差异,允许用统一方式打包不同用途的 WebGPU 接线方案以适应多样化目标环境。 ## 多数 WebGPU 接线具有静态特性 WebGPU 规范以卓越的一致性与优雅设计,解决了图形领域日益混沌复杂的接口问题。该规范着力消除隐式状态,将功能提升至精心设计的 API 调用与配置对象。 对比前文三角形示例的等效 JavaScript 代码(该代码本身由 pngine 生成): ```javascript // (shader-module :name code ...) const code = device.createShaderModule({ code: ` @vertex fn vertexMain( @builtin(vertex_index) VertexIndex : u32 ) -> @builtin(position) vec4f { var pos = array( vec2(0.0, 0.5), vec2(-0.5, -0.5), vec2(0.5, -0.5) ); return vec4f(pos[VertexIndex], 0.0, 1.0); } @fragment fn fragMain() -> @location(0) vec4f { return vec4(1.0, 0.0, 0.0, 1.0); } `, }); // (render-pipeline :name pipeline ...) const pipeline = device.createRenderPipeline({ layout: "auto", vertex: { module: code, entryPoint: "vertexMain" }, fragment: { module: code, entryPoint: "fragMain", targets: [{ format }] }, primitive: { topology: "triangle-list" }, }); // (frame :name main :perform [trianglePass]) const encoder = device.createCommandEncoder(); // (render-pass :name trianglePass ...) const pass = encoder.beginRenderPass({ colorAttachments: [{ view: context.getCurrentTexture().createView(), clearValue: [0, 0, 0, 0], loadOp: "clear", storeOp: "store", }], }); pass.setPipeline(pipeline); pass.draw(3); // (draw :vertex-count 3) pass.end(); device.queue.submit([encoder.finish()]); ``` 这段代码通过创建、接线、排序三个步骤完成 WebGPU 工作流。将 WebGPU 接线方案声明化后,各模块可摆脱顺序依赖,以声明形式存在并灵活重组。 在 pngine 中,`frame` 是少数需要严格排序的环节,其 `:perform` 列表中的名称对应文件其他位置定义的通道: ```lisp (frame :name main :perform [ update-uniforms update-textures sdf-pass post-processing ]) ``` ## 与管线协同工作 [Shadertoy](https://www.shadertoy.com/) 是优秀的着色器共享平台,拥有丰富的示例代码与活跃社区。进阶用户可尝试 [compute.toys](https://compute.toys/),支持多计算着色器协同工作,并通过宏扩展配置管线参数。 如今各类着色器工具层出不穷,但 pngine 的定位不同——它不是交互式开发环境(尽管提供 LSP 支持),而是作为基础平台,支持用户在其上构建场景图、材质节点等高级抽象工具。 ### 从三角形到复杂效果 简单示例仅展示基础功能,以下案例体现 pngine 的实际潜力: **粒子喷泉系统** [下载 PNG 文件](https://hugodaniel.com/images/pngine-particle-fountain.png) 包含 2048 个 GPU 粒子的完整系统:缓冲区池化管理,从喷射口喷射,受重力影响,生命周期结束后以随机速度重生。该示例中属性缓冲区同时作为模拟写入目标。 **实例化植被渲染** [下载 PNG 文件](https://hugodaniel.com/images/pngine-instanced-trees.png) 单次绘制调用渲染 400 棵低多边形树木,包含风力摆动效果与天空深度测试。每棵树由 12 个顶点构成(主干四边形+两个叶丛三角形),400 个实例数据由计算通道一次性填充,最终通过单条绘制指令完成: `(draw :vertex-count 12 :instance-count NUM_TREES)` 这两个案例的 PNG 文件本身即包含完整程序、演示海报及运行时。 ## 打包器与观察者 上述 PNG 文件包含附加二进制数据块,存放实际的 WebGPU 播放器与负载数据,使得需要加载的 JS 代码极其精简。这是导出 pngine .sjon 文件的可选方案,适用于小型静态应用。此外还支持生成 .html 文件、.zip 包或纯字节码。 pngine 中的 "png" 致敬了最初的设计灵感:让图像同时作为预览海报和字节码/运行时载体。 ## 结语 土壤基质需满足三大功能:为根系提供结构支撑、保水透气、储存与输送养分。对我而言,pngine 正是 WebGPU 与图形学的生长基质——作为专注于 WebGPU 的底层实验平台,它既限制在特定领域,又支持通用交互与 CPU(WASM) 代码接入着色器缓冲区,并提供终端/LSP/编辑器环境下的实时验证能力。 这为更高级的实验提供了扎根生长的空间,更支持带有自动 PNG 预览的着色器共享方案。 立即体验 [pngine](https://pngine.com/),或查阅 [源代码](https://github.com/HugoDaniel/pngine)。

相似文章

WebGPU实战教程

Hacker News Top

一本免费的互动书籍,教授使用JavaScript和WebGPU进行图形编程,涵盖从基础到高级主题,如GPU计算和高斯泼溅(Gaussian splatting)。

Gooey:面向 Zig 的 GPU 加速 UI 框架

Hacker News Top

Gooey 是一个面向 Zig 的 GPU 加速 UI 框架,通过 Metal、Vulkan/Wayland 和 WebGPU/WASM 支持 macOS、Linux 和浏览器。它提供声明式 UI、动画、主题、无障碍和零外部依赖。