⚡
sokol-zig と build.zig の構成
以前 zig で OpenGL、そして wasm
という記事を書きましたが、
2024 年の8月 zig-0.13.0
では sokol-zig
が快適です。
sokol-zig
により desktop と wasm 共用の 3D プログラムがさくっとできます。
c の sokol プログラムを zig に写経中。zig
に移植して感じを掴むのがおススメです。
github-action で wasm 生成できました。
手順その1。sokol-zig を自作プログラムで使う
zig init
> mkdir hello
> cd hello
> zig init
sokol-zig への依存を追加
> zig fetch --save=sokol git+https://github.com/floooh/sokol-zig.git
@import("sokol")
できるようにする
src/main.zig から build.zigに追記
const dep_sokol = b.dependency("sokol", .{
.target = target,
.optimize = optimize,
});
exe.root_module.addImport("sokol", dep_sokol.module("sokol"));
src/main.zig で sokol を使う
src/main.zig
const sokol = @import("sokol");
const sg = sokol.gfx;
var pass_action = sg.PassAction{};
export fn init() void {
sg.setup(.{
.environment = sokol.glue.environment(),
.logger = .{ .func = sokol.log.func },
});
pass_action.colors[0] = .{
.load_action = .CLEAR,
.clear_value = .{ .r = 1.0, .g = 0.0, .b = 0.0, .a = 1.0 },
};
}
export fn frame() void {
const g = pass_action.colors[0].clear_value.g + 0.01;
pass_action.colors[0].clear_value.g = if (g > 1.0) 0.0 else g;
sg.beginPass(.{
.action = pass_action,
.swapchain = sokol.glue.swapchain(),
});
sg.endPass();
sg.commit();
}
export fn cleanup() void {
sg.shutdown();
}
pub fn main() void {
sokol.app.run(.{
.init_cb = init,
.frame_cb = frame,
.cleanup_cb = cleanup,
// .event_cb = __dbgui_event,
.width = 400,
.height = 300,
.window_title = "Clear (sokol app)",
.icon = .{ .sokol_default = true },
.logger = .{ .func = sokol.log.func },
});
}
build
> zig build run
これで動けば成功。
sokol-zig は C の sokol よりも簡単かもしれない
sokol
の存在は前から知っていたのだけどビルドシステムがよくわからなくて敬遠しておりました。
しかし sokol-zig
では build.zig
でコード生成をこなしてしまえるので、
本家 c
の sokol
よりプロジェクトの構成が簡単なのです。
手順その2。sokol の shader 変換を含むプロジェクト
最初の例は sokol の動確で CreateWindow まで。
Shader を使って三角形描画するのが真の HelloWorld.
TODO: まとめる
prebuild の sokol-tools-bin をダウンロードし glsl からコード生成する build step を作ります。
*.glsl.zig
# sokol-tools-bin への依存を登録
> zig fetch --save=sokol-tools-bin git+https://github.com/floooh/sokol-tools-bin.git
// a separate step to compile shaders
pub fn sokolShdc(
b: *std.Build,
target: std.Build.ResolvedTarget,
comptime shader: []const u8,
) *std.Build.Step {
const optional_shdc = comptime switch (builtin.os.tag) {
.windows => "win32/sokol-shdc.exe",
.linux => "linux/sokol-shdc",
.macos => if (builtin.cpu.arch.isX86()) "osx/sokol-shdc" else "osx_arm64/sokol-shdc",
else => @panic("unsupported host platform, skipping shader compiler step"),
};
const tools = b.dependency("sokol-tools-bin", .{}); // download して cache される
const shdc_path = tools.path(b.pathJoin(&.{ "bin", optional_shdc })).getPath(b);
const glsl = if (target.result.isDarwin()) "glsl410" else "glsl430";
const slang = glsl ++ ":metal_macos:hlsl5:glsl300es:wgsl";
return &b.addSystemCommand(&.{
shdc_path,
"-i",
shader,
"-o",
shader ++ ".zig",
"-l",
slang,
"-f",
"sokol_zig",
}).step;
}
build.zig
// 下記のようにすると
// "src/hoge.glsl" から "src/hoge.glsl.zig" を生成します
// dependOn により exe より前に生成します。
exe.step.dependOn(sokolShdc(
b,
target,
"src/hoge.glsl",
));
src/main.zig
const shader = @import("hoge.glsl.zig"); // glsl から生成された zig
Discussion