//! // MVK_CONFIG_USE_METAL_ARGUMENT_BUFFERS=1 -- use this if you're getting that odd crash on mac b: *std.Build, nw_builder: *std.Build, target: std.Build.ResolvedTarget, optimize: std.builtin.Mode, nw_mod: *std.Build.Module, spirvReflect: SpirvReflect.SpirvGenerator2, gltf2ozz: ozz.GltfToOzz, options: *std.Build.Step.Options, cookShaders: bool, // list of all shaders discovered under // content/_shaders/def reflectShaderPathList: [][]u8 = undefined, const engineDepList = [_][]const u8{ "assets", "audio", "core", "papyrus", "platform", "physics", }; const BuildSystem = @This(); const std = @import("std"); const Build = std.Build; const LazyPath = Build.LazyPath; const SpirvReflect = @import("SpirvReflect"); const ozz = @import("ozz"); pub const InitOptions = struct { import_name: []const u8 = "Backlog", target: Build.ResolvedTarget, optimize: std.builtin.OptimizeMode, }; pub fn init(b: *std.Build, opts: InitOptions) BuildSystem { const nwdep = b.dependency(opts.import_name, .{ .target = opts.target, .optimize = opts.optimize, }); const self = BuildSystem{ .b = b, .nw_builder = nwdep.builder, .target = opts.target, .optimize = opts.optimize, .nw_mod = nwdep.module("Backlog"), .spirvReflect = SpirvReflect.SpirvGenerator2.init(nwdep.builder, .{}), .options = createGameOptions(b), .gltf2ozz = ozz.GltfToOzz.init(nwdep.builder, .{}), .cookShaders = b.option(bool, "cookShaders", "generates shaders and updates .json files before running the build. (needs to be done whenever shaders are updated, this just runs tools/scripts/cook-shaders.py)") orelse false, }; const exeList = [2]*std.Build.Step.Compile{ self.gltf2ozz.exe, self.spirvReflect.reflect }; const install_tools = b.step("tools", "installs tools needed to generate outputs for the engine"); for (exeList) |exe| { const toolsInstall = b.addInstallArtifact(exe, .{ .dest_dir = .{ .override = .{ .custom = "tools" } }, }); install_tools.dependOn(&toolsInstall.step); } { const runArtifact = b.addRunArtifact(self.gltf2ozz.exe); if (b.args) |args| { runArtifact.addArgs(args); } const run_exe = b.step("gltf2ozz", "runs the gltf animation converter."); run_exe.dependOn(&runArtifact.step); } { const runArtifact = b.addRunArtifact(self.spirvReflect.reflect); if (b.args) |args| { runArtifact.addArgs(args); } const run_exe = b.step("spv-reflect", "runs the gltf animation converter."); run_exe.dependOn(&runArtifact.step); } return self; } pub const AddProgramOptions = struct { name: []const u8, desc: []const u8, root_source_file: LazyPath, imports: []const Build.Module.Import = &.{}, }; pub fn addProgram(self: *BuildSystem, opts: AddProgramOptions) *std.Build.Step.Compile { const b = self.b; const exe = self.nw_builder.addExecutable(.{ .name = opts.name, .target = self.target, .optimize = self.optimize, .root_source_file = self.nw_builder.path("engine/main.zig"), }); b.installArtifact(exe); const runArtifact = b.addRunArtifact(exe); if (b.args) |args| { runArtifact.addArgs(args); } const run_exe = b.step(self.b.fmt("run-{s}", .{opts.name}), opts.desc); run_exe.dependOn(&runArtifact.step); // main path = name/main.zig const mod = b.addModule(opts.name, .{ .target = self.target, .optimize = self.optimize, .root_source_file = opts.root_source_file, .imports = opts.imports, }); exe.root_module.addImport("main", mod); exe.root_module.addImport("Backlog", self.nw_mod); mod.addImport("Backlog", self.nw_mod); exe.root_module.addOptions("BacklogOptions", self.options); if (self.cookShaders) {} // I want to generate definitions from the spirv-reflect-tool during pre-build. // // shaders will now be part of content, not code. However. .zig code definitions will be generated // from content via the build.zig script. // // heres some thoughts: // // -Dupdate_shaders=true // // -Dupdate_shaders basically adds a run step to the build process 'python', 'tools/scripts/cook-shaders.py' // // 1. ensure spirv-reflect is compiled. // 2. shadercross cooker generates all shader outputs - specifically spv files. they are output to content/_shaders/ // - search paths for shaders: // - content/shaders/** // - engine//shaders/** // - shaders must have a unique name across the entire project, naming scheme should be .shadername..hlsl // 3. spirv-cross --reflect is called on each shader, updating the content/_shaders/defs/shader-name.json folder with up to date .json files // // final tree // // engine/ // - ui/ // - shaders/ // - papyrus.rect.vert.hlsl // content/ // - _shaders/ // - def/ // papyrus.rect.vert.json // - msl/ // papyrus.rect.vert.msl // - spv/ // papyrus.rect.vert.spv // - dxil/ // papyrus.rect.vert.dxil // // // During build: // 1. spirv-reflect is called on each json in each folder and .zig shader interfaces added to the executable as a global import. // 2. if -Dupdate_shaders is not // // At any time: // asset-cooker will cook shaders and place them under content/_shaders//. // self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/triangle_mesh.vert"), "triangle_mesh_vert"); // self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/default_lit.frag"), "default_lit"); // self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/debug.vert"), "debug_vert"); // self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/debug.frag"), "debug_frag"); // self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/skybox/skybox.vert"), "skybox_vert"); // self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/graphics/shaders/skybox/skybox.frag"), "skybox_frag"); // self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/PapyrusRect.vert"), "papyrus_vk_vert"); // self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/PapyrusRect.frag"), "papyrus_vk_frag"); // self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/FontSDF.vert"), "FontSDF_vert"); // self.spirvReflect.addShaderInstallRef(exe, self.nw_builder.path("engine/ui/shaders/FontSDF.frag"), "FontSDF_frag"); b.getInstallStep().dependOn(self.nw_builder.getInstallStep()); return exe; } pub fn createGameOptions(b: *std.Build) *std.Build.Step.Options { const opts = b.addOptions(); // build options for core.zig opts.addOption( bool, "mutex_job_queue", b.option(bool, "mutex_job_queue", "temporary test, reverts to old mutex based queue behaviour in jobs.zig:JobManager") orelse false, ); opts.addOption( bool, "zero_logging", b.option(bool, "zero_logging", "disables all logging, only intended for use on job dispatch testing") orelse false, ); opts.addOption( bool, "slow_logging", b.option(bool, "slow_logging", "Disables buffered logging, takes a hit to performance but gain timing information on logging") orelse false, ); opts.addOption( bool, "force_mailbox", b.option(bool, "force_mailbox", "forces mailbox mode for present mode. unlocks framerate to irresponsible levels") orelse false, ); opts.addOption( bool, "use_renderthread", true, ); return opts; } // ========= standalone build instance ======= // maybe it should be an engine launcher or something.. pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{}); const spirvDep = b.dependency("SpirvReflect", .{ .target = target, .optimize = optimize, }); _ = spirvDep; const mod = b.addModule("Backlog", .{ .target = target, .optimize = optimize, .root_source_file = b.path("engine/backlog.zig"), }); for (engineDepList) |depName| { const dep = b.dependency( depName, .{ .target = target, .optimize = optimize, }, ); mod.addImport(depName, dep.module(depName)); } }