diff --git a/build.zig b/build.zig index 7bc8960..910f31d 100644 --- a/build.zig +++ b/build.zig @@ -25,6 +25,10 @@ pub fn build(b: *std.Build) void { .link_libc = true, }); rendering.addIncludePath(b.path("ext")); + if (target.result.os.tag == .macos) { + rendering.addSystemIncludePath(.{ .cwd_relative = "/usr/local/include" }); + rendering.addLibraryPath(.{ .cwd_relative = "/usr/local/lib" }); + } rendering.addCSourceFile(.{ .file = b.path("ext/stb_image.c") }); rendering.addImport("math", math); @@ -45,8 +49,12 @@ pub fn build(b: *std.Build) void { .optimize = optimize, }), }); - sideros.addIncludePath(b.path("ext")); - sideros.addIncludePath(b.path("src")); + sideros.root_module.addIncludePath(b.path("ext")); + sideros.root_module.addIncludePath(b.path("src")); + if (target.result.os.tag == .macos) { + sideros.root_module.addSystemIncludePath(.{ .cwd_relative = "/usr/local/include" }); + sideros.root_module.addLibraryPath(.{ .cwd_relative = "/usr/local/lib" }); + } sideros.root_module.addImport("mods", mods); sideros.root_module.addImport("ecs", ecs); @@ -71,18 +79,18 @@ pub fn build(b: *std.Build) void { }), }); exe.root_module.addIncludePath(b.path("src")); - exe.linkLibrary(sideros); - exe.linkLibC(); - exe.linkSystemLibrary("vulkan"); + exe.root_module.linkLibrary(sideros); + exe.root_module.link_libc = true; + exe.root_module.linkSystemLibrary("vulkan", .{ .needed = true }); if (wayland) { exe.root_module.addIncludePath(b.path("ext")); - exe.linkSystemLibrary("wayland-client"); - exe.linkSystemLibrary("xkbcommon"); + exe.root_module.linkSystemLibrary("wayland-client", .{}); + exe.root_module.linkSystemLibrary("xkbcommon", .{}); exe.root_module.addCSourceFile(.{ .file = b.path("ext/xdg-shell.c") }); } else { - exe.linkSystemLibrary("xcb"); - exe.linkSystemLibrary("xcb-keysyms"); - exe.linkSystemLibrary("xcb-icccm"); + exe.root_module.linkSystemLibrary("xcb", .{}); + exe.root_module.linkSystemLibrary("xcb-keysyms", .{}); + exe.root_module.linkSystemLibrary("xcb-icccm", .{}); } b.installArtifact(exe); @@ -106,14 +114,14 @@ pub fn build(b: *std.Build) void { }), }); exe.root_module.addIncludePath(b.path("src")); - exe.linkSystemLibrary("vulkan"); + exe.root_module.linkSystemLibrary("vulkan", .{}); exe.root_module.addSystemIncludePath(.{ .cwd_relative = "/usr/local/include" }); exe.root_module.addLibraryPath(.{ .cwd_relative = "/usr/local/lib" }); exe.root_module.addCSourceFile(.{.file = b.path("src/window.m")}); exe.root_module.linkFramework("Cocoa", .{}); exe.root_module.linkFramework("Metal", .{}); exe.root_module.linkFramework("QuartzCore", .{}); - exe.linkLibrary(sideros); + exe.root_module.linkLibrary(sideros); b.installArtifact(exe); const run_cmd = b.addRunArtifact(exe); @@ -156,13 +164,10 @@ pub fn build(b: *std.Build) void { } fn compileAllShaders(b: *std.Build, module: *std.Build.Module) void { - const shaders_dir = if (@hasDecl(@TypeOf(b.build_root.handle), "openIterableDir")) - b.build_root.handle.openIterableDir("assets/shaders", .{}) catch @panic("Failed to open shaders directory") - else - b.build_root.handle.openDir("assets/shaders", .{ .iterate = true }) catch @panic("Failed to open shaders directory"); + const shaders_dir = b.build_root.handle.openDir(b.graph.io, "assets/shaders", .{ .iterate = true }) catch @panic("Failed to open shaders directory"); var file_it = shaders_dir.iterate(); - while (file_it.next() catch @panic("Failed to iterate shader directory")) |entry| { + while (file_it.next(b.graph.io) catch @panic("Failed to iterate shader directory")) |entry| { if (entry.kind == .file) { const ext = std.fs.path.extension(entry.name); const basename = std.fs.path.basename(entry.name); diff --git a/src/ecs/entities.zig b/src/ecs/entities.zig index 6f8f2b3..bb1c841 100644 --- a/src/ecs/entities.zig +++ b/src/ecs/entities.zig @@ -33,27 +33,20 @@ pub const Pool = struct { allocator: Allocator, system_groups: std.ArrayList(SystemGroup), sync_groups: std.ArrayList(SyncGroup), - thread_pool: *std.Thread.Pool, - wait_group: std.Thread.WaitGroup, - mutex: std.Thread.Mutex, + threaded_io: std.Io.Threaded, pub fn init(allocator: Allocator, resources: *Resources) !@This() { - var pool = @This(){ + const pool = @This(){ .humans = .{}, .resources = resources, .system_groups = std.ArrayList(SystemGroup).empty, .sync_groups = std.ArrayList(SystemGroup).empty, - .thread_pool = try allocator.create(std.Thread.Pool), - .wait_group = .{}, - .mutex = .{}, + .threaded_io = .init(allocator, .{ + .concurrent_limit = .limited(4), + }), .allocator = allocator, }; - try pool.thread_pool.init(.{ - .allocator = allocator, - .n_jobs = 4, - }); - return pool; } @@ -70,28 +63,37 @@ pub const Pool = struct { self.system_groups.deinit(self.allocator); self.sync_groups.deinit(self.allocator); - self.thread_pool.deinit(); - self.allocator.destroy(self.thread_pool); + self.threaded_io.deinit(); } pub fn tick(self: *@This()) void { + const io = self.threaded_io.io(); + var group: std.Io.Group = .init; + for (0..self.system_groups.items.len) |i| { - self.thread_pool.spawnWg(&self.wait_group, struct { - fn run(pool: *Pool, index: usize) void { - const group = pool.system_groups.items[index]; - for (group) |system| { + group.concurrent(io, struct { + fn run(pool: *Pool, index: usize) std.Io.Cancelable!void { + const system_group = pool.system_groups.items[index]; + for (system_group) |system| { // TODO: system errors should be correctly handled system(pool) catch unreachable; } } - }.run, .{ self, i }); + }.run, .{ self, i }) catch { + const system_group = self.system_groups.items[i]; + for (system_group) |system| { + system(self) catch unreachable; + } + }; } for (0..self.sync_groups.items.len) |i| { - const group = self.sync_groups.items[i]; - for (group) |system| { + const system_group = self.sync_groups.items[i]; + for (system_group) |system| { system(self) catch unreachable; } } + + group.await(io) catch unreachable; } fn getEntities(self: *@This(), T: type) *std.MultiArrayList(T) { diff --git a/src/macos.zig b/src/macos.zig index 59322db..e802ec6 100644 --- a/src/macos.zig +++ b/src/macos.zig @@ -149,18 +149,18 @@ fn vulkan_cleanup(gameInit: sideros.GameInit) void { //c.vkDestroyInstance(gameInit.instance, null); } -pub fn main() !void { +pub fn main(init: std.process.Init) !void { create_window(); const layer = get_metal_layer(); - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + var gpa: std.heap.DebugAllocator(.{}) = .init; const allocator = gpa.allocator(); defer if (gpa.deinit() != .ok) @panic("Memory leaked"); const gameInit = try vulkan_init(allocator, layer); defer vulkan_cleanup(gameInit); - sideros.sideros_init(gameInit); + sideros.sideros_init(init.io, gameInit); while (!is_window_closed()) { poll_cocoa_events(); diff --git a/src/mods/ir.zig b/src/mods/ir.zig index 2dcd8a7..45eedde 100644 --- a/src/mods/ir.zig +++ b/src/mods/ir.zig @@ -27,12 +27,9 @@ const DIndex = packed struct { x: u32, y: u32, }; -comptime { - // TODO: is this too big? we could do with 32 bits and a bit more indirection - std.debug.assert(@sizeOf(Index) == 8); -} -/// packed union has no tag -const Index = packed union { + +/// This intentionally remains tagless; the opcode determines how to interpret it. +const Index = union { u32: u32, i32: i32, u64: u64, diff --git a/src/rendering/GraphicsPipeline.zig b/src/rendering/GraphicsPipeline.zig index ae43342..7224515 100644 --- a/src/rendering/GraphicsPipeline.zig +++ b/src/rendering/GraphicsPipeline.zig @@ -61,8 +61,8 @@ pub const Builder = struct { }; } - pub fn addMesh(self: *Builder, path: []const u8) !Mesh { - const gltf_data = try gltf.parseFile(self.allocator, path); + pub fn addMesh(self: *Builder, io: std.Io, path: []const u8) !Mesh { + const gltf_data = try gltf.parseFile(self.allocator, io, path); const vertex_buffer = try createVertexBuffer(self.allocator, self.device, gltf_data); const index_buffer = try createIndexBuffer(self.allocator, self.device, gltf_data); diff --git a/src/rendering/Renderer.zig b/src/rendering/Renderer.zig index fe9dd7c..ed6d1f6 100644 --- a/src/rendering/Renderer.zig +++ b/src/rendering/Renderer.zig @@ -20,10 +20,10 @@ terrain_pipeline: vk.TerrainPipeline, current_frame: u32, mesh: Mesh, transforms: std.ArrayList(math.Transform), -previous_time: std.time.Instant, +previous_time: std.Io.Timestamp, current_image: usize, -pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_handle: vk.c.VkSurfaceKHR) !Self { +pub fn init(allocator: Allocator, io: std.Io, instance_handle: vk.c.VkInstance, surface_handle: vk.c.VkSurfaceKHR) !Self { const instance: vk.Instance = .{ .handle = instance_handle }; const surface: vk.Surface = .{ .handle = surface_handle }; var physical_device = try vk.PhysicalDevice.pick(allocator, instance); @@ -48,7 +48,7 @@ pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_hand _ = try pipeline_builder.addTexture(texture, diffuse); _ = try pipeline_builder.addTexture(texture, diffuse); - const mesh = try pipeline_builder.addMesh("assets/models/cube.glb"); + const mesh = try pipeline_builder.addMesh(io, "assets/models/cube.glb"); var graphics_pipeline = try pipeline_builder.build(swapchain, render_pass, vertex_shader, fragment_shader); const terrain_vertex_shader = try device.initShader("terrain_vert"); @@ -96,7 +96,7 @@ pub fn init(allocator: Allocator, instance_handle: vk.c.VkInstance, surface_hand .terrain_pipeline = terrain_pipeline, .current_frame = 0, .transforms = transforms, - .previous_time = try std.time.Instant.now(), + .previous_time = std.Io.Timestamp.now(std.Io.Threaded.global_single_threaded.io(), .awake), .mesh = mesh, .current_image = undefined, }; diff --git a/src/rendering/gltf.zig b/src/rendering/gltf.zig index 7073695..7a3971c 100644 --- a/src/rendering/gltf.zig +++ b/src/rendering/gltf.zig @@ -161,8 +161,8 @@ pub const Model = struct { }; }; -pub fn parseFile(allocator: Allocator, name: []const u8) !struct { vertices: [][3]f32, normals: [][3]f32, uvs: [][2]f32, indices: []u16 } { - const file = try std.fs.cwd().openFile(name, .{}); +pub fn parseFile(allocator: Allocator, io: std.Io, name: []const u8) !struct { vertices: [][3]f32, normals: [][3]f32, uvs: [][2]f32, indices: []u16 } { + const file = try std.Io.Dir.cwd().openFile(name, io, .{}); const all = try file.readToEndAlloc(allocator, 1_000_000); defer allocator.free(all); const json_chunk = std.mem.bytesAsValue(Model.Chunk, all[Model.Header.offset..]); diff --git a/src/sideros.zig b/src/sideros.zig index cf016fe..08dc809 100644 --- a/src/sideros.zig +++ b/src/sideros.zig @@ -12,7 +12,7 @@ const std = @import("std"); const systems = @import("systems.zig"); -var gpa = std.heap.GeneralPurposeAllocator(.{}){}; +var gpa: std.heap.DebugAllocator(.{}) = .{}; const allocator = gpa.allocator(); var pool: ecs.Pool = undefined; var renderer: Renderer = undefined; @@ -26,7 +26,7 @@ const ModInfo = struct { runtime: *mods.Runtime, modIdx: u32, }; -var loadedMods: std.ArrayListUnmanaged(ModInfo) = .{}; +var loadedMods: std.ArrayListUnmanaged(ModInfo) = .empty; fn openOrCreateDir(fs: std.fs.Dir, path: []const u8) !std.fs.Dir { var dir: std.fs.Dir = undefined; @@ -133,7 +133,7 @@ fn init_mods() void { } } -export fn sideros_init(init: api.GameInit) callconv(.c) void { +fn sideros_init(io: std.Io, init: api.GameInit) void { resources = .{ .camera = &camera, .renderer = undefined, @@ -145,7 +145,7 @@ export fn sideros_init(init: api.GameInit) callconv(.c) void { ecs.hooks.addHook(.scroll, systems.zoomCamera) catch @panic("TODO handle this"); pool = ecs.Pool.init(allocator, &resources) catch @panic("TODO: Gracefully handle error"); // TODO(ernesto): I think this @ptrCast are unavoidable but maybe not? - renderer = Renderer.init(allocator, @ptrCast(init.instance), @ptrCast(init.surface)) catch |err| std.debug.panic("TODO: Gracefully handle error: {}\n", .{err}); + renderer = Renderer.init(allocator, io, @ptrCast(init.instance), @ptrCast(init.surface)) catch |err| std.debug.panic("TODO: Gracefully handle error: {}\n", .{err}); resources.terrain = rendering.Terrain.init(allocator, renderer.device, .{ .octaves = 8, diff --git a/src/systems.zig b/src/systems.zig index 8b708f4..1b5c1b0 100644 --- a/src/systems.zig +++ b/src/systems.zig @@ -8,8 +8,8 @@ pub fn render(pool: *ecs.Pool) anyerror!void { const camera = pool.resources.camera; const terrain = pool.resources.terrain; - const now = try std.time.Instant.now(); - const delta_time: f32 = @as(f32, @floatFromInt(now.since(renderer.previous_time))) / @as(f32, 1_000_000_000.0); + const now = std.Io.Timestamp.now(pool.threaded_io.io(), .awake); + const delta_time: f32 = @as(f32, @floatFromInt(renderer.previous_time.durationTo(now).toNanoseconds())) / @as(f32, 1_000_000_000.0); pool.resources.delta_time = delta_time; renderer.previous_time = now;