资讯动态

用 Odin 主进程驱动 WGPU 原生化沙盒:Electrobun Odin Alchemy Sandbox 模板深度解析

发布时间:2026/9/15 17:29:18 来源:尧图企业网站定制
用 Odin 主进程驱动 WGPU 原生化沙盒Electrobun Odin Alchemy Sandbox 模板深度解析【免费下载链接】electrobunBuild ultra fast, tiny, and cross-platform desktop apps with Typescript.项目地址: https://gitcode.com/GitHub_Trending/el/electrobunOdin Alchemy Sandbox 是 Electrobun 仓库中一个「零 npm 依赖」的原生模板它以 Odin 编写桌面应用主进程承载一套确定性deterministic的 320 × 180 落沙falling-sand元胞模拟并将模拟结果渲染到原生electrobun-wgpu表面上。阅读本文后你将掌握hutch run dev/hutch run build的运行与构建流程、electrobun.config.ts中 Odin 主进程与 WGPU 打包的配置方式、webview 与原生主进程之间的 RPC/单向消息协作模型以及「固定容量状态 确定性细胞遍历 密度感知移动 直接 Dawn ABI」这一整套零分配热路径的工程实现。模板概览一个由 Odin 掌管的交互式落沙世界该模板位于 templates/odin-alchemy-wgpu核心结构如下templates/odin-alchemy-wgpu/ ├── src/ │ ├── mainview/ # webview 侧 UIindex.html / index.css / index.ts │ └── odin/ │ ├── main.odin # Odin 主进程模拟 WGPU 渲染 RPC │ └── sim_test.odin # 确定性物理规则测试 ├── electrobun.config.ts # Electrobun 构建配置 ├── hutch.config.ts # Hutch 脚本dev / build └── tsconfig.jsonREADME 对它的定位是一个由 Odin 主进程持有、渲染在原生electrobun-wgpu表面上的交互式 Electrobun 落沙沙盒。也就是说这个模板的“游戏循环”与渲染完全绕开 JavaScript 运行时由 Odin 原生存取 320 × 180 的元胞网格再以 WGPU 实例化绘制上屏——webview 只负责控制面板与指针输入。快速开始运行与构建README 给出的开发命令是hutch run devhutch run dev会构建应用、启动它并在源文件发生变化时自动重建watch 模式。具体脚本定义在 hutch.config.tsexport default { scripts: { start: [hutch, electrobun, dev], dev: [hutch, electrobun, dev, --watch], watch: [hutch, electrobun, dev, --watch], build: [hutch, electrobun, build, --envstable], }, };生产构建契约是hutch run buildREADME 明确指出这个原生模板没有任何 npm 依赖——webview 侧代码由 Electrobun 的构建管线直接编译打包。build脚本会生成生产包在此过程中WGPU 会被打进应用包而 CEFChromium Embedded Framework在 macOS、Linux、Windows 上均被禁用因为本应用不需要浏览器渲染引擎的全部能力。构建配置拆解Odin 主进程与 WGPU 打包electrobun.config.ts 是理解整个模板的关键import type { ElectrobunConfig } from electrobun; export default { app: { name: odin-alchemy-wgpu, identifier: odinalchemywgpu.electrobun.dev, version: 0.0.1, }, build: { mainProcess: odin, odin: { entrypoint: src/odin/main.odin, }, views: { mainview: { entrypoint: src/mainview/index.ts, }, }, copy: { src/mainview/index.html: views/mainview/index.html, src/mainview/index.css: views/mainview/index.css, }, mac: { bundleCEF: false, bundleWGPU: true, }, linux: { bundleCEF: false, bundleWGPU: true, }, win: { bundleCEF: false, bundleWGPU: true, }, }, } satisfies ElectrobunConfig;要点解读mainProcess: odin声明主进程由 Odin 编译入口是 src/odin/main.odin。从源码看main :: proc()依次完成electrobun.load()加载 core、resolveBundlePaths()解析资源路径、创建窗口与 webview然后启动三个协作单元create_ui线程、drain_host_message_queue消息泵线程最后进入electrobun.runMainThread主事件循环。views.mainview指向 webview 的 TypeScript 入口copy把index.html与index.css原样复制到views/mainview/下供 webview 以views://mainview/index.html加载见 main.odin 中webview_options.url views://mainview/index.html。三平台一致的bundleCEF: falsebundleWGPU: true这解释了 README 中「WGPU 已打包、CEF 已禁用」的说法。由于渲染完全由 WGPU 完成应用不需要 CEF 提供的完整 Web 渲染内核。架构分工webview 拥有 UIOdin 拥有世界README 用一句话划清了边界The webview owns the compact controls and normalized pointer input. Odin owns the grid, interaction rules, fixed-step clock, WGPU pipeline, and statistics.在 index.html 中可以看到具体形态electrobun-wgpu idalchemy-surface passthrough masks.topbar, .material-dock, .settings, .stats /electrobun-wgpu div classpaint-layer idpaint-layer aria-labelAlchemy simulation canvas/divelectrobun-wgpu是原生 WGPU 表面元素masks属性把顶栏、材质坞、设置面板和统计栏区域“挖空”让这些 DOM 控件浮在原生渲染之上透明覆盖层.paint-layer负责捕获指针输入。前端逻辑在 index.ts 中等待electrobun-wgpu自定义元素就绪customElements.whenDefined拿到wgpuViewId后通过 RPCstartGpu启动原生渲染线程用ResizeObserverrequestAnimationFrame节流地把表面尺寸通过configureGpu同步给主进程指针事件被归一化为 0~1 的浮点坐标normalizedPoint通过单向消息paintStroke发送避免拖拽时产生请求-响应承诺链README 明确提到Brush traffic uses one-way host messages so pointer movement does not create a chain of request promises界面底栏的 FPS / Cells / Tick / Surface 统计由主进程以simStats消息推送刷新。RPC 契约的类型定义在 index.ts 的AlchemyRPC中webview 向主进程发起startGpu、configureGpu、resetSim、stepSim四个请求主进程向 webview 发送simStats消息与接收paintStroke消息。模拟系统320 × 180 的确定性元胞世界README 对模拟的描述可以完整转述为可操作的事实Paint sand, water, fire, seeds, oil, stone, or empty space into a deterministic 320 x 180 cellular world.对应到 main.odin 的常量与枚举GRID_WIDTH :: 320 GRID_HEIGHT :: 180 CELL_COUNT :: GRID_WIDTH * GRID_HEIGHT DEFAULT_SEED :: u32(0x10203040) Material :: enum u8 { Empty 0, Sand 1, Water 2, Fire 3, Plant 4, Oil 5, Stone 6, }每种材质的物理行为都有对应的process_*函数与 README 完全一一对应Sand沙process_sand向下落下方为水/油/火时执行交换swap_cells即“沙子排开液体”下方非空时尝试对角滑落try_diagonal。Water水process_water向下落遇到油则交换下沉对角受阻后向两侧平移最多 3 格try_sideways(sim, x, y, 3)。Oil油process_oil遇到相邻火焰有概率被点燃has_adjacent(...Fire)ignite_cell否则下落、对角滑落、横向流动 2 格。由于水与油交换的规则油会聚集在水之上无需单独的浮力通道。Fire火process_fire相邻有水即熄灭否则以 1/3 概率点燃四邻ignite_neighbors生命周期为26 shadetick且会向上飘升。Plant种子/植物process_plant未扎根age PLANT_ROOTED_AGE即 1000时寻找半径 5 内的水来“扎根”已扎根后以tick % 7 25% 概率在水源半径 6 内向上生长出新植物。Stone石永久地形不参与更新。sim_step是固定步进时钟的核心每 tick 先自增tick随后自底向上逐行扫描并交替左右扫描方向避免移动方向性偏差每个细胞只有在updated[index] ! sim.tick时才处理防止一个细胞在一 tick 内被移动两次火则在第二轮专门遍历中更新。README 中「Horizontal scan direction alternates while per-cell tick stamps prevent a moved cell from updating twice」正是这段逻辑。sim_resetsim_populate_demo用给定种子铺设同一个起始场景底部石板、四根石柱、一潭水、一片油、一座沙丘、一列植物和一排火焰——README 说「Applying a numeric seed resets the same starter scene and random sequence」即同一种子必然复现同一场景与同一随机序列。Odin 技术要点零分配热路径的工程实现README 归纳了五条 Odin 侧关键技术这里结合源码逐条印证1. 固定容量状态Fixed-capacity stateSim :: struct { cells: [CELL_COUNT]Cell, // 57,600 个固定数组单元 updated: [CELL_COUNT]u32, // tick 印章防止重复更新 tick: u32, rng: u32, occupied: int, }网格、更新印章、有界笔刷队列[MAX_PAINT_COMMANDS]Paint_Command上限 512全部是固定数组模拟热路径sim_step、process_*、move_cell、swap_cells不进行任何分配。2. 确定性细胞顺序Deterministic cellular orderingrand_u32是经典的 xorshift32x ~ x 13 x ~ x 17 x ~ x 5它同时驱动横向滑落/流动的方向选择与每个细胞的颜色变化shade u8(rand_u32(sim.rng) 0xff)。由于 RNG 状态被严格保存在Sim内、扫描顺序确定、tick 印章防重整个演化是可复现的——这一点被 sim_test.odin 中的reset_is_deterministic测试直接验证同一种子两次sim_reset后全部 57,600 个细胞逐一相等。3. 密度感知移动Density-aware movementtry_diagonal中沙可以交换水、油、火水可以交换油process_sand里沙会与下方液体直接交换。这一组规则让“油浮于水、沙沉于底”的密度分层自然涌现README 所说「oil therefore collects above water without a separate buoyancy pass」与代码完全吻合。4. 低竞争主机输入Low-contention host inputRPC 回调enqueue_paint只在sync.mutex保护下追加紧凑的笔刷命令模拟线程每帧在互斥锁内一次性排空有界队列for index in 0 .. paint_count随后在锁外调用apply_paint_command把连续笔迹光栅化成网格圆。apply_paint_command对起点到终点做线性插值steps : max(abs(dx), abs(dy))每个采样点调用paint_circle而paint_circle对非空材质有约 6%rand % 100 94的随机跳过制造沙粒洒落的自然感。5. 直接 Dawn ABIDirect Dawn ABIOdin 通过dynlib.symbol_address从electrobun.WgpuNative.symbols.__handle中按名加载它需要的 20 个 WGPU C 符号wgpuDeviceCreateShaderModule、wgpuDeviceCreateRenderPipeline、wgpuQueueWriteBuffer、wgpuQueueSubmit、wgpuSurfaceGetCapabilities等见wgpu_api_load。所有 C 结构体布局如Wgpu_Render_Pipeline_Descriptor、Wgpu_Surface_Configuration都通过#assert(size_of(...))与#assert(offset_of(...))在编译期校验——README 说「verifies descriptor layouts at compile time」这样一旦 Electrobun 捆绑的 WGPU 库与 Odin SDK 版本不匹配构建会直接失败而不是运行时崩溃。渲染管线实例化四边形绘制 57,600 格世界gpu_render_loop是独立的渲染线程thread.create_and_start(gpu_render_loop, self_cleanup true)每帧流程为加锁排空笔刷命令与参数paused / step / reset 请求若 WGPU 上下文未建立或视图 ID 变化则调用electrobun.createForWgpuView创建上下文、getQueue取队列、pick_surface_configuration读取表面能力尺寸变化时configure_surface以PRESENT_MODE_FIFO配置交换链非暂停状态执行一次sim_steppack_instances把每个非空细胞打包成 8 个 f32 的实例属性归一化中心坐标、半宽/半高、RGBA 颜色写入实例缓冲区render_frame中wgpuQueueWriteBuffer上传实例数据 →beginRenderPass清屏色{0.009, 0.010, 0.012, 1.0}→draw(6, instance_count)一次绘制全部实例 →end→queue_submit→wgpuSurfacePresentMainThread。每个细胞渲染为一个四边形角落缓冲区CORNER_VERTEX_COUNT 6两个三角形按顶点步进实例缓冲区按实例步进。WGSL 着色器内联在 main.odin 的CELL_SHADER字符串中顶点着色器把center corner * size投影到裁剪空间片元着色器用smoothstep(0.70, 1.0, edge)做出圆角细胞效果。颜色由cell_color生成——沙有明暗变化、水波随时间正弦波动、火苗按(tick shade) % 9闪烁、植物在扎根前后呈现不同绿色——这些细节让 320 × 180 的格子世界有了视觉层次。帧率统计每帧累加、每秒换算一次fps_value每 15 帧向 webview 推送一次simStatsFPS、占用细胞数、tick、表面尺寸也就是界面底栏那组实时数据的来源。控制方式与交互README 的 Controls 一节对应 index.html 与 index.ts 的实现材质选择调色板中的 Sand / Water / Fire / Seed / Oil / Stone / Erase 七项快捷键 1~7materialOrder [1, 2, 3, 4, 5, 6, 0]笔刷大小1~18 的滑块默认 6主进程侧enqueue_paint同样以clamp(radius, 1, 18)约束绘画在paint-layer上按住左键绘画、按住右键擦除event.button 2 ? 0 : selectedMaterialpointermove持续发送paintStrokePause冻结材质更新但不禁用绘画params.paused只控制sim_step是否执行快捷键空格Step先暂停再推进恰好一个确定性 tickstepSimRPC快捷键.Reset / Seed输入种子并应用重置同一个起始场景与随机序列resetSim种子范围 1~4294967295快捷键R。测试验证物理规则与确定性有据可查sim_test.odin 用 Odin 的core:testing框架固化了 README 描述的物理行为sand_displaces_water沙落入水中一个 tick 后水被沙替换、沙原位变成水——验证“沙排开液体”water_sinks_below_oil水沉到油下方油上浮——验证“密度感知移动”与油水分层water_extinguishes_fire火旁有水则一个 tick 后火格变为空——验证“水灭火”reset_is_deterministic同一种子两次重置后 57,600 格逐一相等——验证确定性承诺paint_command_rasterizes_continuous_stroke一条 0.1→0.9 的连续笔迹能光栅化出超过 900 个占用细胞——验证笔刷线段插值逻辑。这组测试把 README 里“sand falls and piles, water seeks low open space, oil floats over water and burns, fire rises and expires, seeds settle and grow when they find water, and stone forms permanent terrain”的每一句描述都落到了可执行断言上。平台注意事项README 明确列出的平台约束源码与配置均可佐证Odin 编译器由 Hutch/Electrobun 内置vendoredOdin 处于 pre-1.0 阶段外部编译器发布可能引入破坏性变更因此工具链版本由仓库锁定参见仓库中的kitchen/scripts/check-odin-test-mirrors.ts等工具链校验脚本。Windows需要 Visual Studio Build Tools仅支持 x64。macOS需要 Xcode Command Line Tools。Linux需要clang。不做交叉编译原生主进程构建只运行在目标平台上每个平台本地构建、本地运行。参考资料模板 READMEtemplates/odin-alchemy-wgpu/README.mdOdin 主进程完整实现templates/odin-alchemy-wgpu/src/odin/main.odin物理规则测试templates/odin-alchemy-wgpu/src/odin/sim_test.odinwebview 侧 UI 与 RPCtemplates/odin-alchemy-wgpu/src/mainview/index.ts、index.html、index.css构建配置electrobun.config.ts、hutch.config.ts总的来说Odin Alchemy Sandbox 是一个展示 Electrobun「原生主进程 原生 WGPU 表面 轻量 webview」三层架构的完整样例它证明了用 Odin 编写桌面主进程、以零分配热路径承载确定性模拟、并通过直接 Dawn ABI 把结果画上原生表面的可行性也为想在自己应用中接入electrobun-wgpu的开发者提供了一份可直接对照的参考实现。【免费下载链接】electrobunBuild ultra fast, tiny, and cross-platform desktop apps with Typescript.项目地址: https://gitcode.com/GitHub_Trending/el/electrobun创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价