mirror of
https://github.com/bdnugget/zig-gamedev.git
synced 2026-11-07 09:07:31 +00:00
488 lines
19 KiB
Zig
488 lines
19 KiB
Zig
const builtin = @import("builtin");
|
|
const std = @import("std");
|
|
|
|
pub const min_zig_version = std.SemanticVersion{ .major = 0, .minor = 11, .patch = 0, .pre = "dev.3859" };
|
|
|
|
pub fn build(b: *std.Build) void {
|
|
//
|
|
// Options and system checks
|
|
//
|
|
ensureZigVersion() catch return;
|
|
const options = Options{
|
|
.optimize = b.standardOptimizeOption(.{}),
|
|
.target = b.standardTargetOptions(.{}),
|
|
.zd3d12_enable_debug_layer = b.option(
|
|
bool,
|
|
"zd3d12-enable-debug-layer",
|
|
"Enable DirectX 12 debug layer",
|
|
) orelse false,
|
|
.zd3d12_enable_gbv = b.option(
|
|
bool,
|
|
"zd3d12-enable-gbv",
|
|
"Enable DirectX 12 GPU-Based Validation (GBV)",
|
|
) orelse false,
|
|
.zpix_enable = b.option(bool, "zpix-enable", "Enable PIX for Windows profiler") orelse false,
|
|
};
|
|
ensureTarget(options.target) catch return;
|
|
ensureGit(b.allocator) catch return;
|
|
ensureGitLfs(b.allocator, "install") catch return;
|
|
ensureGitLfs(b.allocator, "pull") catch return;
|
|
ensureGitLfsContent("/samples/triangle_wgpu/triangle_wgpu_content/Roboto-Medium.ttf") catch return;
|
|
|
|
// Fetch the latest Dawn/WebGPU binaries.
|
|
const skip_dawn_update = b.option(bool, "skip-dawn-update", "Skip updating Dawn binaries") orelse false;
|
|
if (!skip_dawn_update) {
|
|
var child = std.ChildProcess.init(&.{ "git", "submodule", "update", "--init", "--remote" }, b.allocator);
|
|
child.cwd = thisDir();
|
|
child.stderr = std.io.getStdErr();
|
|
child.stdout = std.io.getStdOut();
|
|
_ = child.spawnAndWait() catch {
|
|
std.log.err("Failed to fetch git submodule. Please try to re-clone.", .{});
|
|
return;
|
|
};
|
|
}
|
|
ensureGitLfsContent("/libs/zgpu/libs/dawn/x86_64-windows-gnu/dawn.lib") catch return;
|
|
|
|
//
|
|
// Packages
|
|
//
|
|
packagesCrossPlatform(b, options);
|
|
|
|
if (options.target.isWindows() and
|
|
(builtin.target.os.tag == .windows or builtin.target.os.tag == .linux))
|
|
{
|
|
packagesWindowsLinux(b, options);
|
|
|
|
if (builtin.target.os.tag == .windows) {
|
|
packagesWindows(b, options);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Sample applications
|
|
//
|
|
samplesCrossPlatform(b, options);
|
|
|
|
if (options.target.isWindows() and
|
|
(builtin.target.os.tag == .windows or builtin.target.os.tag == .linux))
|
|
{
|
|
samplesWindowsLinux(b, options);
|
|
|
|
if (builtin.target.os.tag == .windows) {
|
|
samplesWindows(b, options);
|
|
}
|
|
}
|
|
|
|
//
|
|
// Tests
|
|
//
|
|
tests(b, options);
|
|
|
|
//
|
|
// Benchmarks
|
|
//
|
|
benchmarks(b, options);
|
|
|
|
//
|
|
// Experiments
|
|
//
|
|
if (b.option(bool, "experiments", "Build our prototypes and experimental programs") orelse false) {
|
|
@import("experiments/build.zig").build(b, options);
|
|
}
|
|
}
|
|
|
|
fn packagesCrossPlatform(b: *std.Build, options: Options) void {
|
|
const target = options.target;
|
|
const optimize = options.optimize;
|
|
|
|
zopengl_pkg = zopengl.package(b, target, optimize, .{});
|
|
zmath_pkg = zmath.package(b, target, optimize, .{});
|
|
zpool_pkg = zpool.package(b, target, optimize, .{});
|
|
zglfw_pkg = zglfw.package(b, target, optimize, .{});
|
|
zsdl_pkg = zsdl.package(b, target, optimize, .{});
|
|
zmesh_pkg = zmesh.package(b, target, optimize, .{});
|
|
znoise_pkg = znoise.package(b, target, optimize, .{});
|
|
zstbi_pkg = zstbi.package(b, target, optimize, .{});
|
|
zbullet_pkg = zbullet.package(b, target, optimize, .{});
|
|
zgui_pkg = zgui.package(b, target, optimize, .{
|
|
.options = .{ .backend = .glfw_wgpu },
|
|
});
|
|
zgpu_pkg = zgpu.package(b, target, optimize, .{
|
|
.options = .{ .uniforms_buffer_size = 4 * 1024 * 1024 },
|
|
.deps = .{ .zpool = zpool_pkg.zpool, .zglfw = zglfw_pkg.zglfw },
|
|
});
|
|
ztracy_pkg = ztracy.package(b, target, optimize, .{
|
|
.options = .{
|
|
.enable_ztracy = !target.isDarwin(), // TODO: ztracy fails to compile on macOS.
|
|
.enable_fibers = !target.isDarwin(),
|
|
},
|
|
});
|
|
zphysics_pkg = zphysics.package(b, target, optimize, .{});
|
|
zaudio_pkg = zaudio.package(b, target, optimize, .{});
|
|
zflecs_pkg = zflecs.package(b, target, optimize, .{});
|
|
}
|
|
|
|
fn packagesWindowsLinux(b: *std.Build, options: Options) void {
|
|
const target = options.target;
|
|
const optimize = options.optimize;
|
|
|
|
zwin32_pkg = zwin32.package(b, target, optimize, .{});
|
|
zd3d12_pkg = zd3d12.package(b, target, optimize, .{
|
|
.options = .{
|
|
.enable_debug_layer = options.zd3d12_enable_debug_layer,
|
|
.enable_gbv = options.zd3d12_enable_gbv,
|
|
.upload_heap_capacity = 32 * 1024 * 1024,
|
|
},
|
|
.deps = .{ .zwin32 = zwin32_pkg.zwin32 },
|
|
});
|
|
zpix_pkg = zpix.package(b, target, optimize, .{
|
|
.options = .{ .enable = options.zpix_enable },
|
|
.deps = .{ .zwin32 = zwin32_pkg.zwin32 },
|
|
});
|
|
common_pkg = common.package(b, target, optimize, .{
|
|
.deps = .{ .zwin32 = zwin32_pkg.zwin32, .zd3d12 = zd3d12_pkg.zd3d12 },
|
|
});
|
|
}
|
|
|
|
fn packagesWindows(b: *std.Build, options: Options) void {
|
|
const target = options.target;
|
|
const optimize = options.optimize;
|
|
|
|
zd3d12_d2d_pkg = zd3d12.package(b, target, optimize, .{
|
|
.options = .{
|
|
.enable_debug_layer = options.zd3d12_enable_debug_layer,
|
|
.enable_gbv = options.zd3d12_enable_gbv,
|
|
.enable_d2d = true,
|
|
},
|
|
.deps = .{ .zwin32 = zwin32_pkg.zwin32 },
|
|
});
|
|
common_d2d_pkg = common.package(b, target, optimize, .{
|
|
.deps = .{ .zwin32 = zwin32_pkg.zwin32, .zd3d12 = zd3d12_d2d_pkg.zd3d12 },
|
|
});
|
|
zxaudio2_pkg = zxaudio2.package(b, target, optimize, .{
|
|
.options = .{ .enable_debug_layer = options.zd3d12_enable_debug_layer },
|
|
.deps = .{ .zwin32 = zwin32_pkg.zwin32 },
|
|
});
|
|
}
|
|
|
|
fn samplesCrossPlatform(b: *std.Build, options: Options) void {
|
|
const minimal_gl = @import("samples/minimal_gl/build.zig");
|
|
const triangle_wgpu = @import("samples/triangle_wgpu/build.zig");
|
|
const procedural_mesh_wgpu = @import("samples/procedural_mesh_wgpu/build.zig");
|
|
const textured_quad_wgpu = @import("samples/textured_quad_wgpu/build.zig");
|
|
const physically_based_rendering_wgpu = @import("samples/physically_based_rendering_wgpu/build.zig");
|
|
const bullet_physics_test_wgpu = @import("samples/bullet_physics_test_wgpu/build.zig");
|
|
const audio_experiments_wgpu = @import("samples/audio_experiments_wgpu/build.zig");
|
|
const gui_test_wgpu = @import("samples/gui_test_wgpu/build.zig");
|
|
const instanced_pills_wgpu = @import("samples/instanced_pills_wgpu/build.zig");
|
|
const layers_wgpu = @import("samples/layers_wgpu/build.zig");
|
|
const gamepad_wgpu = @import("samples/gamepad_wgpu/build.zig");
|
|
const physics_test_wgpu = @import("samples/physics_test_wgpu/build.zig");
|
|
const monolith = @import("samples/monolith/build.zig");
|
|
|
|
install(b, minimal_gl.build(b, options), "minimal_gl");
|
|
install(b, triangle_wgpu.build(b, options), "triangle_wgpu");
|
|
install(b, textured_quad_wgpu.build(b, options), "textured_quad_wgpu");
|
|
install(b, gui_test_wgpu.build(b, options), "gui_test_wgpu");
|
|
install(b, physically_based_rendering_wgpu.build(b, options), "physically_based_rendering_wgpu");
|
|
install(b, instanced_pills_wgpu.build(b, options), "instanced_pills_wgpu");
|
|
install(b, gamepad_wgpu.build(b, options), "gamepad_wgpu");
|
|
install(b, layers_wgpu.build(b, options), "layers_wgpu");
|
|
install(b, bullet_physics_test_wgpu.build(b, options), "bullet_physics_test_wgpu");
|
|
install(b, procedural_mesh_wgpu.build(b, options), "procedural_mesh_wgpu");
|
|
install(b, physics_test_wgpu.build(b, options), "physics_test_wgpu");
|
|
install(b, monolith.build(b, options), "monolith");
|
|
install(b, audio_experiments_wgpu.build(b, options), "audio_experiments_wgpu");
|
|
}
|
|
|
|
fn samplesWindowsLinux(b: *std.Build, options: Options) void {
|
|
const minimal_d3d12 = @import("samples/minimal_d3d12/build.zig");
|
|
const textured_quad = @import("samples/textured_quad/build.zig");
|
|
const triangle = @import("samples/triangle/build.zig");
|
|
const mesh_shader_test = @import("samples/mesh_shader_test/build.zig");
|
|
const rasterization = @import("samples/rasterization/build.zig");
|
|
const bindless = @import("samples/bindless/build.zig");
|
|
const simple_raytracer = @import("samples/simple_raytracer/build.zig");
|
|
|
|
install(b, minimal_d3d12.build(b, options), "minimal_d3d12");
|
|
install(b, bindless.build(b, options), "bindless");
|
|
install(b, triangle.build(b, options), "triangle");
|
|
install(b, simple_raytracer.build(b, options), "simple_raytracer");
|
|
install(b, textured_quad.build(b, options), "textured_quad");
|
|
install(b, rasterization.build(b, options), "rasterization");
|
|
install(b, mesh_shader_test.build(b, options), "mesh_shader_test");
|
|
}
|
|
|
|
fn samplesWindows(b: *std.Build, options: Options) void {
|
|
const audio_playback_test = @import("samples/audio_playback_test/build.zig");
|
|
const audio_experiments = @import("samples/audio_experiments/build.zig");
|
|
const vector_graphics_test = @import("samples/vector_graphics_test/build.zig");
|
|
const directml_convolution_test = @import("samples/directml_convolution_test/build.zig");
|
|
|
|
install(b, vector_graphics_test.build(b, options), "vector_graphics_test");
|
|
install(b, directml_convolution_test.build(b, options), "directml_convolution_test");
|
|
install(b, audio_playback_test.build(b, options), "audio_playback_test");
|
|
install(b, audio_experiments.build(b, options), "audio_experiments");
|
|
}
|
|
|
|
fn tests(b: *std.Build, options: Options) void {
|
|
const test_step = b.step("test", "Run all tests");
|
|
|
|
test_step.dependOn(zmath.runTests(b, options.optimize, options.target));
|
|
test_step.dependOn(zmesh.runTests(b, options.optimize, options.target));
|
|
test_step.dependOn(zstbi.runTests(b, options.optimize, options.target));
|
|
test_step.dependOn(znoise.runTests(b, options.optimize, options.target));
|
|
test_step.dependOn(zglfw.runTests(b, options.optimize, options.target));
|
|
test_step.dependOn(zpool.runTests(b, options.optimize, options.target));
|
|
test_step.dependOn(zjobs.runTests(b, options.optimize, options.target));
|
|
test_step.dependOn(zaudio.runTests(b, options.optimize, options.target));
|
|
test_step.dependOn(zflecs.runTests(b, options.optimize, options.target));
|
|
test_step.dependOn(zphysics.runTests(b, options.optimize, options.target));
|
|
}
|
|
|
|
fn benchmarks(b: *std.Build, options: Options) void {
|
|
const benchmark_step = b.step("benchmark", "Run all benchmarks");
|
|
|
|
benchmark_step.dependOn(zmath.runBenchmarks(b, options.target));
|
|
}
|
|
|
|
pub var zmath_pkg: zmath.Package = undefined;
|
|
pub var znoise_pkg: znoise.Package = undefined;
|
|
pub var zopengl_pkg: zopengl.Package = undefined;
|
|
pub var zsdl_pkg: zsdl.Package = undefined;
|
|
pub var zpool_pkg: zpool.Package = undefined;
|
|
pub var zmesh_pkg: zmesh.Package = undefined;
|
|
pub var zglfw_pkg: zglfw.Package = undefined;
|
|
pub var zstbi_pkg: zstbi.Package = undefined;
|
|
pub var zbullet_pkg: zbullet.Package = undefined;
|
|
pub var zgui_pkg: zgui.Package = undefined;
|
|
pub var zgpu_pkg: zgpu.Package = undefined;
|
|
pub var ztracy_pkg: ztracy.Package = undefined;
|
|
pub var zphysics_pkg: zphysics.Package = undefined;
|
|
pub var zaudio_pkg: zaudio.Package = undefined;
|
|
pub var zflecs_pkg: zflecs.Package = undefined;
|
|
|
|
pub var zwin32_pkg: zwin32.Package = undefined;
|
|
pub var zd3d12_pkg: zd3d12.Package = undefined;
|
|
pub var zpix_pkg: zpix.Package = undefined;
|
|
pub var zxaudio2_pkg: zxaudio2.Package = undefined;
|
|
pub var common_pkg: common.Package = undefined;
|
|
pub var common_d2d_pkg: common.Package = undefined;
|
|
pub var zd3d12_d2d_pkg: zd3d12.Package = undefined;
|
|
|
|
const zsdl = @import("libs/zsdl/build.zig");
|
|
const zopengl = @import("libs/zopengl/build.zig");
|
|
const zmath = @import("libs/zmath/build.zig");
|
|
const zglfw = @import("libs/zglfw/build.zig");
|
|
const zpool = @import("libs/zpool/build.zig");
|
|
const zjobs = @import("libs/zjobs/build.zig");
|
|
const zmesh = @import("libs/zmesh/build.zig");
|
|
const znoise = @import("libs/znoise/build.zig");
|
|
const zstbi = @import("libs/zstbi/build.zig");
|
|
const zwin32 = @import("libs/zwin32/build.zig");
|
|
const zd3d12 = @import("libs/zd3d12/build.zig");
|
|
const zxaudio2 = @import("libs/zxaudio2/build.zig");
|
|
const zpix = @import("libs/zpix/build.zig");
|
|
const common = @import("libs/common/build.zig");
|
|
const zbullet = @import("libs/zbullet/build.zig");
|
|
const zgui = @import("libs/zgui/build.zig");
|
|
const zgpu = @import("libs/zgpu/build.zig");
|
|
const ztracy = @import("libs/ztracy/build.zig");
|
|
const zphysics = @import("libs/zphysics/build.zig");
|
|
const zaudio = @import("libs/zaudio/build.zig");
|
|
const zflecs = @import("libs/zflecs/build.zig");
|
|
|
|
pub const Options = struct {
|
|
optimize: std.builtin.Mode,
|
|
target: std.zig.CrossTarget,
|
|
|
|
zd3d12_enable_debug_layer: bool,
|
|
zd3d12_enable_gbv: bool,
|
|
|
|
zpix_enable: bool,
|
|
};
|
|
|
|
fn install(b: *std.Build, exe: *std.Build.CompileStep, comptime name: []const u8) void {
|
|
// TODO: Problems with LTO on Windows.
|
|
exe.want_lto = false;
|
|
if (exe.optimize == .ReleaseFast)
|
|
exe.strip = true;
|
|
|
|
//comptime var desc_name: [256]u8 = [_]u8{0} ** 256;
|
|
//comptime _ = std.mem.replace(u8, name, "", "", desc_name[0..]);
|
|
//comptime var desc_size = std.mem.indexOf(u8, &desc_name, "\x00").?;
|
|
|
|
const install_step = b.step(name, "Build '" ++ name ++ "' demo");
|
|
install_step.dependOn(&b.addInstallArtifact(exe).step);
|
|
|
|
const run_step = b.step(name ++ "-run", "Run '" ++ name ++ "' demo");
|
|
const run_cmd = b.addRunArtifact(exe);
|
|
run_cmd.step.dependOn(install_step);
|
|
run_step.dependOn(&run_cmd.step);
|
|
|
|
b.getInstallStep().dependOn(install_step);
|
|
}
|
|
|
|
fn ensureZigVersion() !void {
|
|
var installed_ver = @import("builtin").zig_version;
|
|
installed_ver.build = null;
|
|
|
|
if (installed_ver.order(min_zig_version) == .lt) {
|
|
std.log.err("\n" ++
|
|
\\---------------------------------------------------------------------------
|
|
\\
|
|
\\Installed Zig compiler version is too old.
|
|
\\
|
|
\\Min. required version: {any}
|
|
\\Installed version: {any}
|
|
\\
|
|
\\Please install newer version and try again.
|
|
\\Latest version can be found here: https://ziglang.org/download/
|
|
\\
|
|
\\---------------------------------------------------------------------------
|
|
\\
|
|
, .{ min_zig_version, installed_ver });
|
|
return error.ZigIsTooOld;
|
|
}
|
|
}
|
|
|
|
fn ensureTarget(cross: std.zig.CrossTarget) !void {
|
|
const target = (std.zig.system.NativeTargetInfo.detect(cross) catch unreachable).target;
|
|
|
|
const supported = switch (target.os.tag) {
|
|
.windows => target.cpu.arch.isX86() and target.abi.isGnu(),
|
|
.linux => (target.cpu.arch.isX86() or target.cpu.arch.isAARCH64()) and target.abi.isGnu(),
|
|
.macos => blk: {
|
|
if (!target.cpu.arch.isX86() and !target.cpu.arch.isAARCH64()) break :blk false;
|
|
|
|
// If min. target macOS version is lesser than the min version we have available, then
|
|
// our Dawn binary is incompatible with the target.
|
|
if (target.os.version_range.semver.min.order(
|
|
.{ .major = 12, .minor = 0, .patch = 0 },
|
|
) == .lt) break :blk false;
|
|
break :blk true;
|
|
},
|
|
else => false,
|
|
};
|
|
if (!supported) {
|
|
std.log.err("\n" ++
|
|
\\---------------------------------------------------------------------------
|
|
\\
|
|
\\Unsupported build target. Dawn/WebGPU binary for this target is not available.
|
|
\\
|
|
\\Following targets are supported:
|
|
\\
|
|
\\x86_64-windows-gnu
|
|
\\x86_64-linux-gnu
|
|
\\x86_64-macos.12-none
|
|
\\aarch64-linux-gnu
|
|
\\aarch64-macos.12-none
|
|
\\
|
|
\\---------------------------------------------------------------------------
|
|
\\
|
|
, .{});
|
|
return error.TargetNotSupported;
|
|
}
|
|
}
|
|
|
|
fn ensureGit(allocator: std.mem.Allocator) !void {
|
|
const printErrorMsg = (struct {
|
|
fn impl() void {
|
|
std.log.err("\n" ++
|
|
\\---------------------------------------------------------------------------
|
|
\\
|
|
\\'git version' failed. Is Git not installed?
|
|
\\
|
|
\\---------------------------------------------------------------------------
|
|
\\
|
|
, .{});
|
|
}
|
|
}).impl;
|
|
const argv = &[_][]const u8{ "git", "version" };
|
|
const result = std.ChildProcess.exec(.{
|
|
.allocator = allocator,
|
|
.argv = argv,
|
|
.cwd = thisDir(),
|
|
}) catch { // e.g. FileNotFound
|
|
printErrorMsg();
|
|
return error.GitNotFound;
|
|
};
|
|
defer {
|
|
allocator.free(result.stderr);
|
|
allocator.free(result.stdout);
|
|
}
|
|
if (result.term.Exited != 0) {
|
|
printErrorMsg();
|
|
return error.GitNotFound;
|
|
}
|
|
}
|
|
|
|
fn ensureGitLfs(allocator: std.mem.Allocator, cmd: []const u8) !void {
|
|
const printNoGitLfs = (struct {
|
|
fn impl() void {
|
|
std.log.err("\n" ++
|
|
\\---------------------------------------------------------------------------
|
|
\\
|
|
\\Please install Git LFS (Large File Support) extension and run 'zig build' again.
|
|
\\
|
|
\\For more info about Git LFS see: https://git-lfs.github.com/
|
|
\\
|
|
\\---------------------------------------------------------------------------
|
|
\\
|
|
, .{});
|
|
}
|
|
}).impl;
|
|
const argv = &[_][]const u8{ "git", "lfs", cmd };
|
|
const result = std.ChildProcess.exec(.{
|
|
.allocator = allocator,
|
|
.argv = argv,
|
|
.cwd = thisDir(),
|
|
}) catch { // e.g. FileNotFound
|
|
printNoGitLfs();
|
|
return error.GitLfsNotFound;
|
|
};
|
|
defer {
|
|
allocator.free(result.stderr);
|
|
allocator.free(result.stdout);
|
|
}
|
|
if (result.term.Exited != 0) {
|
|
printNoGitLfs();
|
|
return error.GitLfsNotFound;
|
|
}
|
|
}
|
|
|
|
fn ensureGitLfsContent(comptime file_path: []const u8) !void {
|
|
const printNoGitLfsContent = (struct {
|
|
fn impl() void {
|
|
std.log.err("\n" ++
|
|
\\---------------------------------------------------------------------------
|
|
\\
|
|
\\Something went wrong, Git LFS content has not been downloaded.
|
|
\\
|
|
\\Please try to re-clone the repo and build again.
|
|
\\
|
|
\\---------------------------------------------------------------------------
|
|
\\
|
|
, .{});
|
|
}
|
|
}).impl;
|
|
const file = std.fs.openFileAbsolute(thisDir() ++ file_path, .{}) catch {
|
|
printNoGitLfsContent();
|
|
return error.GitLfsNoContent;
|
|
};
|
|
defer file.close();
|
|
|
|
const size = file.getEndPos() catch {
|
|
printNoGitLfsContent();
|
|
return error.GitLfsNoContent;
|
|
};
|
|
if (size <= 1024) {
|
|
printNoGitLfsContent();
|
|
return error.GitLfsNoContent;
|
|
}
|
|
}
|
|
|
|
inline fn thisDir() []const u8 {
|
|
return comptime std.fs.path.dirname(@src().file) orelse ".";
|
|
}
|