diff --git a/build.zig b/build.zig index 574ad5b..1826f14 100644 --- a/build.zig +++ b/build.zig @@ -25,12 +25,24 @@ pub fn build(b: *std.Build) void { .use_pkg_config = .force, }); exe.linkLibC(); - - b.installArtifact(exe); exe.root_module.addImport("vk", vkmod); - const run_cmd = b.addRunArtifact(exe); + b.installArtifact(exe); + var docs_dir: std.Build.GeneratedFile = .{ + .path = "docs", + .step = &exe.step, + }; + exe.generated_docs = &docs_dir; + const docs = b.addInstallDirectory(.{ + .source_dir = .{ .generated = &docs_dir }, + .install_dir = .{ .custom = "docs" }, + .install_subdir = "", + }); + const docs_step = b.step("docs", "Build the docs"); + docs_step.dependOn(&docs.step); + + const run_cmd = b.addRunArtifact(exe); run_cmd.step.dependOn(b.getInstallStep()); if (b.args) |args| { @@ -45,6 +57,12 @@ pub fn build(b: *std.Build) void { .target = target, .optimize = optimize, }); + exe_unit_tests.linkSystemLibrary2("glfw3", .{ + .needed = true, + .preferred_link_mode = .static, + .use_pkg_config = .force, + }); + exe_unit_tests.linkLibC(); const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); diff --git a/src/Context.zig b/src/Context.zig index 6a138fd..1652be0 100644 --- a/src/Context.zig +++ b/src/Context.zig @@ -10,7 +10,10 @@ const USE_DEBUG_LAYERS = switch (builtin.mode) { .ReleaseSmall, .ReleaseFast => false, }; -allocator: std.mem.Allocator, +const MAX_DEVICES = 16; +const MAX_DEVICE_EXTENSIONS = 512; +const MAX_INSTANCE_EXTENSIONS = 64; +const MAX_LAYERS = 512; vkb: BaseDispatch, vki: InstanceDispatch, @@ -28,52 +31,79 @@ surface: vk.SurfaceKHR, messenger: if (USE_DEBUG_LAYERS) vk.DebugUtilsMessengerEXT else void, -pub fn init(allocator: std.mem.Allocator, window: *c.GLFWwindow) !Self { +fn Enumeration(comptime T: type, comptime cap: u32) type { + return struct { + buf: [cap]T = undefined, + len: u32 = 0, + + const FULL: @This() = .{ .len = cap }; + const EMPTY: @This() = .{ .len = 0 }; + + pub fn slice(self: anytype) switch (@TypeOf(&self.buf)) { + *[cap]T => []T, + *const [cap]T => []const T, + else => unreachable, + } { + return self.buf[0..self.len]; + } + + pub fn appendSlice(self: *@This(), source: []const T) !void { + if (self.len + source.len > cap) return error.Overflow; + @memcpy(self.buf[self.len..][0..source.len], source); + self.len += @intCast(source.len); + } + + pub fn append(self: *@This(), val: T) !void { + if (self.len + 1 > cap) return error.Overflow; + self.buf[self.len] = val; + self.len += 1; + } + }; +} + +pub fn init(window: *c.GLFWwindow) !Self { var self: Self = undefined; - self.allocator = allocator; self.vkb = try BaseDispatch.load(&c.glfwGetInstanceProcAddress); const vkb = self.vkb; - var req_exts = std.ArrayList([*:0]const u8).init(allocator); - defer req_exts.deinit(); + var req_exts = Enumeration([*:0]const u8, MAX_INSTANCE_EXTENSIONS).EMPTY; + var req_layers = Enumeration([*:0]const u8, MAX_LAYERS).EMPTY; + var req_dev_exts = Enumeration([*:0]const u8, MAX_DEVICE_EXTENSIONS).EMPTY; - var req_layers = std.ArrayList([*:0]const u8).init(allocator); - defer req_layers.deinit(); - - var req_device_exts = std.ArrayList([*:0]const u8).init(allocator); - defer req_device_exts.deinit(); + try req_dev_exts.append("VK_KHR_swapchain"); if (USE_DEBUG_LAYERS) { try req_layers.append("VK_LAYER_KHRONOS_validation"); try req_exts.append("VK_EXT_debug_utils"); } - try req_device_exts.append("VK_KHR_swapchain"); + { + var glfw_ext_count: u32 = 0; + const glfw_exts: [*][*:0]const u8 = @ptrCast(c.glfwGetRequiredInstanceExtensions(&glfw_ext_count)); + try req_exts.appendSlice(glfw_exts[0..glfw_ext_count]); + } - var glfw_ext_count: u32 = 0; - const glfw_exts: [*][*:0]const u8 = @ptrCast(c.glfwGetRequiredInstanceExtensions(&glfw_ext_count)); - try req_exts.appendSlice(glfw_exts[0..glfw_ext_count]); + std.log.debug("requesting extensions: {s}", .{req_exts.slice()}); + std.log.debug("requesting layers: {s}", .{req_layers.slice()}); + std.log.debug("requesting device extensions: {s}", .{req_dev_exts.slice()}); - std.log.debug("requesting extensions: {s}", .{req_exts.items}); - std.log.debug("requesting layers: {s}", .{req_layers.items}); - std.log.debug("requesting device extensions: {s}", .{req_device_exts.items}); + var available_exts = Enumeration(vk.ExtensionProperties, MAX_INSTANCE_EXTENSIONS).FULL; + _ = try vkb.enumerateInstanceExtensionProperties( + null, + &available_exts.len, + &available_exts.buf, + ); - var available_ext_count: u32 = 0; - _ = try vkb.enumerateInstanceExtensionProperties(null, &available_ext_count, null); - const available_exts = try allocator.alloc(vk.ExtensionProperties, available_ext_count); - defer allocator.free(available_exts); - _ = try vkb.enumerateInstanceExtensionProperties(null, &available_ext_count, available_exts.ptr); + var available_layers = Enumeration(vk.LayerProperties, MAX_LAYERS).FULL; + _ = try vkb.enumerateInstanceLayerProperties( + &available_layers.len, + &available_layers.buf, + ); - var available_layer_count: u32 = 0; - _ = try vkb.enumerateInstanceLayerProperties(&available_layer_count, null); - const available_layers = try allocator.alloc(vk.LayerProperties, available_layer_count); - defer allocator.free(available_layers); - _ = try vkb.enumerateInstanceLayerProperties(&available_layer_count, available_layers.ptr); - - for (req_exts.items) |name| { + for (req_exts.slice()) |name| { const required_name = std.mem.sliceTo(name, 0); - for (available_exts) |prop| { + for (available_exts.slice()) |prop| { const available_name = std.mem.sliceTo(&prop.extension_name, 0); if (std.mem.eql(u8, required_name, available_name)) break; } else { @@ -81,9 +111,9 @@ pub fn init(allocator: std.mem.Allocator, window: *c.GLFWwindow) !Self { } } - for (req_layers.items) |name| { + for (req_layers.slice()) |name| { const required_name = std.mem.sliceTo(name, 0); - for (available_layers) |prop| { + for (available_layers.slice()) |prop| { const available_name = std.mem.sliceTo(&prop.layer_name, 0); if (std.mem.eql(u8, required_name, available_name)) break; } else { @@ -118,10 +148,10 @@ pub fn init(allocator: std.mem.Allocator, window: *c.GLFWwindow) !Self { const instance_create_info = vk.InstanceCreateInfo{ .p_application_info = &app_info, - .enabled_extension_count = @intCast(req_exts.items.len), - .pp_enabled_extension_names = req_exts.items.ptr, - .enabled_layer_count = @intCast(req_layers.items.len), - .pp_enabled_layer_names = req_layers.items.ptr, + .enabled_extension_count = req_exts.len, + .pp_enabled_extension_names = &req_exts.buf, + .enabled_layer_count = req_layers.len, + .pp_enabled_layer_names = &req_layers.buf, .p_next = if (USE_DEBUG_LAYERS) &debug_create_info else null, }; @@ -155,15 +185,12 @@ pub fn init(allocator: std.mem.Allocator, window: *c.GLFWwindow) !Self { } errdefer vki.destroySurfaceKHR(self.instance, self.surface, null); - var device_count: u32 = 0; - _ = try vki.enumeratePhysicalDevices(self.instance, &device_count, null); - const devices = try allocator.alloc(vk.PhysicalDevice, device_count); - defer allocator.free(devices); - _ = try vki.enumeratePhysicalDevices(self.instance, &device_count, devices.ptr); - - var available_device_exts_count: u32 = 0; - var available_device_exts = std.ArrayList(vk.ExtensionProperties).init(allocator); - defer available_device_exts.deinit(); + var devices = Enumeration(vk.PhysicalDevice, MAX_DEVICES).FULL; + _ = try vki.enumeratePhysicalDevices( + self.instance, + &devices.len, + &devices.buf, + ); // todo some ranking strategy to find the most-suitable device const Selection = struct { @@ -171,7 +198,7 @@ pub fn init(allocator: std.mem.Allocator, window: *c.GLFWwindow) !Self { props: vk.PhysicalDeviceProperties, feats: vk.PhysicalDeviceFeatures, }; - const selected: Selection = find_device: for (devices) |device| { + const selected: Selection = find_device: for (devices.slice()) |device| { const props = vki.getPhysicalDeviceProperties(device); const feats = vki.getPhysicalDeviceFeatures(device); @@ -179,23 +206,17 @@ pub fn init(allocator: std.mem.Allocator, window: *c.GLFWwindow) !Self { // if (feats.geometry_shader == vk.FALSE) continue; + var available_dev_exts = Enumeration(vk.ExtensionProperties, MAX_DEVICE_EXTENSIONS).FULL; _ = try vki.enumerateDeviceExtensionProperties( device, null, - &available_device_exts_count, - null, - ); - try available_device_exts.resize(available_device_exts_count); - _ = try vki.enumerateDeviceExtensionProperties( - device, - null, - &available_device_exts_count, - available_device_exts.items.ptr, + &available_dev_exts.len, + &available_dev_exts.buf, ); - for (req_device_exts.items) |name| { + for (req_dev_exts.slice()) |name| { const required_name = std.mem.sliceTo(name, 0); - for (available_device_exts.items) |prop| { + for (available_dev_exts.slice()) |prop| { const available_name = std.mem.sliceTo(&prop.extension_name, 0); if (std.mem.eql(u8, required_name, available_name)) break; } else { @@ -213,11 +234,12 @@ pub fn init(allocator: std.mem.Allocator, window: *c.GLFWwindow) !Self { return error.NoSuitablePhysicalDevice; }; - var queue_family_count: u32 = 0; - vki.getPhysicalDeviceQueueFamilyProperties(selected.device, &queue_family_count, null); - const queue_family_properties = try allocator.alloc(vk.QueueFamilyProperties, queue_family_count); - defer allocator.free(queue_family_properties); - vki.getPhysicalDeviceQueueFamilyProperties(selected.device, &queue_family_count, queue_family_properties.ptr); + var queue_families = Enumeration(vk.QueueFamilyProperties, 64).FULL; + vki.getPhysicalDeviceQueueFamilyProperties( + selected.device, + &queue_families.len, + &queue_families.buf, + ); // todo this should be incorporated with physical device selection/ranking. const Indices = struct { @@ -228,7 +250,7 @@ pub fn init(allocator: std.mem.Allocator, window: *c.GLFWwindow) !Self { var graphics: ?u32 = null; var present: ?u32 = null; - for (queue_family_properties, 0..) |prop, idx| { + for (queue_families.slice(), 0..) |prop, idx| { if (graphics == null and prop.queue_flags.graphics_bit) { graphics = @intCast(idx); // continue; // forces distinct queue families @@ -258,8 +280,7 @@ pub fn init(allocator: std.mem.Allocator, window: *c.GLFWwindow) !Self { const gp_priorities = [_]f32{ 1.0, 1.0 }; - var queue_create_infos = std.ArrayList(vk.DeviceQueueCreateInfo).init(allocator); - defer queue_create_infos.deinit(); + var queue_create_infos = Enumeration(vk.DeviceQueueCreateInfo, 2).EMPTY; // queue info family indices must be unique. so if the graphics and present queues are the same, create two queues // in the same family. otherwise create queues in separate families. there should probably be some general way to @@ -288,13 +309,13 @@ pub fn init(allocator: std.mem.Allocator, window: *c.GLFWwindow) !Self { } const device_create_info = vk.DeviceCreateInfo{ - .queue_create_info_count = @intCast(queue_create_infos.items.len), - .p_queue_create_infos = queue_create_infos.items.ptr, + .queue_create_info_count = queue_create_infos.len, + .p_queue_create_infos = &queue_create_infos.buf, .p_enabled_features = &selected.feats, - .enabled_extension_count = @intCast(req_device_exts.items.len), - .pp_enabled_extension_names = req_device_exts.items.ptr, - .enabled_layer_count = @intCast(req_layers.items.len), - .pp_enabled_layer_names = req_layers.items.ptr, + .enabled_extension_count = req_dev_exts.len, + .pp_enabled_extension_names = &req_dev_exts.buf, + .enabled_layer_count = req_layers.len, + .pp_enabled_layer_names = &req_layers.buf, }; self.device = try vki.createDevice( diff --git a/src/main.zig b/src/main.zig index 3841f81..1aa6e33 100644 --- a/src/main.zig +++ b/src/main.zig @@ -5,10 +5,6 @@ const vk = @import("vk"); const Context = @import("Context.zig"); pub fn main() !void { - var gpa = std.heap.GeneralPurposeAllocator(.{}){}; - const allocator = gpa.allocator(); - defer _ = gpa.detectLeaks(); - if (c.glfwInit() != c.GLFW_TRUE) { return error.GlfwInitFailed; } @@ -24,7 +20,7 @@ pub fn main() !void { ) orelse return error.glfwCreateWindowFailed; defer c.glfwDestroyWindow(window); - const ctx = try Context.init(allocator, window); + const ctx = try Context.init(window); defer ctx.deinit(); while (c.glfwWindowShouldClose(window) == 0) : (c.glfwPollEvents()) {