From 1613b90ac5b5bc73b4a8adab15ba345301a57f65 Mon Sep 17 00:00:00 2001 From: David Allemang Date: Tue, 9 Jul 2024 14:39:16 -0400 Subject: [PATCH] root module nu_options --- src/main.zig | 11 +- src/nu.zig | 9 + src/nu/Render.zig | 389 +++----------------------------------- src/nu/Render/Context.zig | 388 +++++++++++++++++++++++++++++++++++++ src/nu/Window.zig | 16 +- 5 files changed, 438 insertions(+), 375 deletions(-) create mode 100644 src/nu/Render/Context.zig diff --git a/src/main.zig b/src/main.zig index a075d3e..3a3a79e 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1,9 +1,13 @@ const std = @import("std"); - const nu = @import("nu.zig"); const App = @import("App.zig"); +pub const nu_options: nu.Options = .{ + .window = .{ .title = "Hello World" }, + .render = .{ .app_name = "hello-world" }, +}; + pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.detectLeaks(); @@ -13,7 +17,10 @@ pub fn main() !void { // problem: how to specify runtime options, like Window title? // problem: where should gpa go? probably some "Engine" structure in nu.zig - try nu.Window.init(alloc, .{ .title = "Hello World" }); + // don't necessarily need to declare topological sort - depth-first traversal + // of each module's dependencies without repeats would do. + + try nu.Window.init(alloc); defer nu.Window.deinit(); try nu.Render.init(alloc); diff --git a/src/nu.zig b/src/nu.zig index f3e7dbc..759e098 100644 --- a/src/nu.zig +++ b/src/nu.zig @@ -1,9 +1,18 @@ const std = @import("std"); +const root = @import("root"); pub const Window = @import("nu/Window.zig"); pub const Render = @import("nu/Render.zig"); pub const ImGui = @import("nu/ImGui.zig"); +pub const Options = struct { + window: Window.Options = .{}, + render: Render.Options = .{}, + // imgui: ImGui.Options = .{}, +}; + +pub const options: Options = if (@hasDecl(root, "nu_options")) root.nu_options else .{}; + pub fn run( driver: anytype, modules: anytype, diff --git a/src/nu/Render.zig b/src/nu/Render.zig index 0c54891..47fa3f7 100644 --- a/src/nu/Render.zig +++ b/src/nu/Render.zig @@ -6,378 +6,35 @@ const std = @import("std"); const builtin = @import("builtin"); const vk = @import("vk"); -const Window = @import("Window.zig"); +const ctx = @import("Render/Context.zig"); -pub const RenderOptions = struct { - app_name: []const u8, +pub const Options = struct { + app_name: []const u8 = "nu-au-app", + app_version: struct { + variant: u3 = 0, + major: u7 = 0, + minor: u10 = 0, + patch: u12 = 0, + } = .{}, engine_name: []const u8 = "nu-au", + engine_version: struct { + variant: u3 = 0, + major: u7 = 0, + minor: u10 = 0, + patch: u12 = 0, + } = .{}, + frames_in_flight: u8 = 3, }; -pub const use_debug_messenger = switch (builtin.mode) { - .Debug, .ReleaseSafe => true, - .ReleaseSmall, .ReleaseFast => false, -}; +pub fn init(alloc: std.mem.Allocator) !void { + // todo make ctx not globals -pub const apis: []const vk.ApiInfo = &.{ - vk.features.version_1_0, - vk.features.version_1_1, - vk.features.version_1_2, - vk.features.version_1_3, - vk.extensions.khr_surface, - vk.extensions.khr_swapchain, - vk.extensions.khr_dynamic_rendering, - if (use_debug_messenger) vk.extensions.ext_debug_utils else .{}, -}; - -pub const device_extensions: []const [*:0]const u8 = &.{ - // todo somehow sync this with APIs above? - vk.extensions.khr_swapchain.name, - vk.extensions.khr_dynamic_rendering.name, -}; - -// // todo check vulkan supported -// pub fn frame() void { -// std.debug.print("frame\n", .{}); -// } - -pub const BaseWrapper = vk.BaseWrapper(apis); -pub const InstanceWrapper = vk.InstanceWrapper(apis); -pub const DeviceWrapper = vk.DeviceWrapper(apis); - -pub const InstanceProxy = vk.InstanceProxy(apis); -pub const DeviceProxy = vk.DeviceProxy(apis); -pub const QueueProxy = vk.QueueProxy(apis); -pub const CommandBufferProxy = vk.CommandBufferProxy(apis); - -pub const B: *const BaseWrapper = &_bw; -pub const I: *const InstanceProxy = &_ip; -pub const D: *const DeviceProxy = &_dp; -pub const Q: *const QueueProxy = &_qp; - -pub const device_config: *const CandidateDeviceInfo = &_dconfig; - -var _bw: BaseWrapper = undefined; -var _iw: InstanceWrapper = undefined; -var _dw: DeviceWrapper = undefined; - -var _ip: InstanceProxy = undefined; -var _dp: DeviceProxy = undefined; -var _qp: QueueProxy = undefined; - -var _instance: vk.Instance = undefined; -var _device: vk.Device = undefined; -var _dconfig: CandidateDeviceInfo = undefined; -var _queue: vk.Queue = undefined; -var _surface: vk.SurfaceKHR = undefined; - -pub fn init( - alloc: std.mem.Allocator, -) !void { - try init_base(); - errdefer deinit_base(); - - try init_instance(alloc); - errdefer deinit_instance(); - - try init_device(alloc); - errdefer deinit_device(); + try ctx.init(alloc); + errdefer ctx.deinit(); } +pub fn frame() void {} + pub fn deinit() void { - deinit_device(); - deinit_instance(); - deinit_base(); + ctx.deinit(); } - -fn init_base() !void { - if (use_debug_messenger) { - _bw = try BaseWrapper.load(glfwGetInstanceProcAddress); - } else { - _bw = BaseWrapper.loadNoFail(glfwGetInstanceProcAddress); - } -} - -fn deinit_base() void {} - -fn init_instance(alloc: std.mem.Allocator) !void { - var extensions = std.ArrayList([*:0]const u8).init(alloc); - defer extensions.deinit(); - - var layers = std.ArrayList([*:0]const u8).init(alloc); - defer layers.deinit(); - - if (use_debug_messenger) { - try extensions.appendSlice(&.{ - vk.extensions.ext_debug_utils.name, - }); - - try layers.appendSlice(&.{ - "VK_LAYER_KHRONOS_validation", - }); - } - - var glfw_exts_count: u32 = 0; - const glfw_exts: [*]const [*:0]const u8 = - @ptrCast(glfwGetRequiredInstanceExtensions(&glfw_exts_count)); - try extensions.appendSlice(glfw_exts[0..glfw_exts_count]); - - const mci: vk.DebugUtilsMessengerCreateInfoEXT = .{ - .message_severity = .{ - .error_bit_ext = true, - .info_bit_ext = true, - .verbose_bit_ext = true, - .warning_bit_ext = true, - }, - .message_type = .{ - .device_address_binding_bit_ext = true, - .general_bit_ext = false, - .performance_bit_ext = true, - .validation_bit_ext = true, - }, - .pfn_user_callback = &debug_callback, - .p_user_data = null, - }; - - _instance = try B.createInstance(&.{ - .p_application_info = &.{ - .p_application_name = "zig-glfw-vulkan", // todo RenderOptions - .application_version = vk.makeApiVersion(0, 0, 0, 0), - .p_engine_name = "nu-au", // todo RenderOptions - .engine_version = vk.makeApiVersion(0, 0, 0, 0), - .api_version = vk.API_VERSION_1_3, - }, - .enabled_extension_count = @intCast(extensions.items.len), - .pp_enabled_extension_names = extensions.items.ptr, - .enabled_layer_count = @intCast(layers.items.len), - .pp_enabled_layer_names = layers.items.ptr, - .p_next = if (use_debug_messenger) &mci else null, - }, null); - - if (use_debug_messenger) { - _iw = try InstanceWrapper.load(_instance, _bw.dispatch.vkGetInstanceProcAddr); - } else { - _iw = InstanceWrapper.loadNoFail(_instance, _bw.dispatch.vkGetInstanceProcAddr); - } - - _ip = InstanceProxy.init(_instance, &_iw); - - if (glfwCreateWindowSurface(_instance, Window.handle, null, &_surface) != .success) { - return error.glfwCreateWindowSurfaceFailed; - } -} - -fn deinit_instance() void { - _ip.destroySurfaceKHR(_surface, null); - _ip.destroyInstance(null); -} - -const CandidateDeviceInfo = struct { - pdev: vk.PhysicalDevice, - format: vk.SurfaceFormatKHR, - mode: vk.PresentModeKHR, - family: u32, // must support graphics and present for now - - fn init(alloc: std.mem.Allocator, pdev: vk.PhysicalDevice) !struct { i32, CandidateDeviceInfo } { - var score: i32 = 0; - var res: CandidateDeviceInfo = undefined; - - res.pdev = pdev; - - const props = I.getPhysicalDeviceProperties(pdev); - score += switch (props.device_type) { - vk.PhysicalDeviceType.discrete_gpu => 1000, - vk.PhysicalDeviceType.integrated_gpu => 500, - else => 0, - }; - - var format_count: u32 = undefined; - _ = try I.getPhysicalDeviceSurfaceFormatsKHR(pdev, _surface, &format_count, null); - if (format_count == 0) return error.NoSurfaceFormats; - const formats = try alloc.alloc(vk.SurfaceFormatKHR, format_count); - defer alloc.free(formats); - _ = try I.getPhysicalDeviceSurfaceFormatsKHR(pdev, _surface, &format_count, formats.ptr); - - for (formats) |fmt| { - if (fmt.color_space == .srgb_nonlinear_khr) { - res.format = fmt; - break; - } - } else { - res.format = formats[0]; - score -= 100; - } - - var mode_count: u32 = undefined; - _ = try I.getPhysicalDeviceSurfacePresentModesKHR(pdev, _surface, &mode_count, null); - if (mode_count == 0) return error.NoSurfacePresentModes; - const modes = try alloc.alloc(vk.PresentModeKHR, mode_count); - defer alloc.free(modes); - _ = try I.getPhysicalDeviceSurfacePresentModesKHR(pdev, _surface, &mode_count, modes.ptr); - - if (std.mem.indexOfAny(vk.PresentModeKHR, modes, &.{ - vk.PresentModeKHR.mailbox_khr, - vk.PresentModeKHR.immediate_khr, - })) |idx| { - res.mode = modes[idx]; - } else { - score -= 50; - res.mode = .fifo_khr; // this is guaranteed - } - - var ext_count: u32 = undefined; - _ = try I.enumerateDeviceExtensionProperties(pdev, null, &ext_count, null); - const exts = try alloc.alloc(vk.ExtensionProperties, ext_count); - defer alloc.free(exts); - _ = try I.enumerateDeviceExtensionProperties(pdev, null, &ext_count, exts.ptr); - - for (device_extensions) |needle| { - for (exts) |ext| { - if (std.mem.eql( - u8, - std.mem.span(needle), - std.mem.sliceTo(&ext.extension_name, 0), - )) - break; - } else { - return error.MissingDeviceExtension; - } - } - - var family_count: u32 = undefined; - I.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null); - const families = try alloc.alloc(vk.QueueFamilyProperties, family_count); - defer alloc.free(families); - I.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr); - - for (families, 0..) |prop, idx| { - const graphics_support = prop.queue_flags.graphics_bit; - const present_support = try I.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(idx), _surface) == vk.TRUE; - - if (graphics_support and present_support) { - res.family = @intCast(idx); - break; - } - } else { - return error.NoSuitableFamily; - } - - return .{ score, res }; - } -}; - -fn init_device(alloc: std.mem.Allocator) !void { - var pdev_count: u32 = undefined; - _ = try I.enumeratePhysicalDevices(&pdev_count, null); - if (pdev_count == 0) return error.NoDevice; - const pdevs = try alloc.alloc(vk.PhysicalDevice, pdev_count); - defer alloc.free(pdevs); - _ = try I.enumeratePhysicalDevices(&pdev_count, pdevs.ptr); - - // const scores = std.ArrayList(i32). - var scores: std.MultiArrayList(struct { score: i32, ci: CandidateDeviceInfo }) = .{}; - defer scores.deinit(alloc); - - for (pdevs) |pdev| { - const score, const ci = CandidateDeviceInfo.init(alloc, pdev) catch continue; - try scores.append(alloc, .{ .score = score, .ci = ci }); - } - - const idx = std.sort.argMax(i32, scores.items(.score), {}, std.sort.asc(i32)) orelse - return error.NoSuitableDevice; - _dconfig = scores.get(idx).ci; - - const qci: []const vk.DeviceQueueCreateInfo = &.{ - vk.DeviceQueueCreateInfo{ - .queue_family_index = _dconfig.family, - .queue_count = 1, - .p_queue_priorities = &[_]f32{1.0}, - }, - }; - - _device = try I.createDevice(_dconfig.pdev, &.{ - .queue_create_info_count = @intCast(qci.len), - .p_queue_create_infos = qci.ptr, - .enabled_extension_count = @intCast(device_extensions.len), - .pp_enabled_extension_names = device_extensions.ptr, - .p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{ - .dynamic_rendering = vk.TRUE, - }, - }, null); - - if (use_debug_messenger) { - _dw = try DeviceWrapper.load(_device, _iw.dispatch.vkGetDeviceProcAddr); - } else { - _dw = DeviceWrapper.loadNoFail(_device, _iw.dispatch.vkGetDeviceProcAddr); - } - _dp = DeviceProxy.init(_device, &_dw); - errdefer D.destroyDevice(null); - - _queue = D.getDeviceQueue(_dconfig.family, 0); - - _qp = QueueProxy.init(_queue, &_dw); - - // todo i'm thinking this needs to be a more complex pointer structure... i'm making assumptions here about how the - // command pools are meant to work. probably I am cooking too much. -} - -fn deinit_device() void { - D.destroyDevice(null); -} - -pub fn debug_callback( - msg_severity: vk.DebugUtilsMessageSeverityFlagsEXT, - msg_type: vk.DebugUtilsMessageTypeFlagsEXT, - p_data: ?*const vk.DebugUtilsMessengerCallbackDataEXT, - _: ?*anyopaque, -) callconv(vk.vulkan_call_conv) vk.Bool32 { - // ripped from std.log.defaultLog - - const data = p_data orelse return vk.FALSE; - const message = data.p_message orelse return vk.FALSE; - - const severity_prefix = if (msg_severity.verbose_bit_ext) - "verbose:" - else if (msg_severity.info_bit_ext) - "info:" - else if (msg_severity.warning_bit_ext) - "warning:" - else if (msg_severity.error_bit_ext) - "error:" - else - "?:"; - - const type_prefix = if (msg_type.general_bit_ext) - "" - else if (msg_type.validation_bit_ext) - "validation:" - else if (msg_type.performance_bit_ext) - "performance:" - else if (msg_type.device_address_binding_bit_ext) - "device_address_binding:" - else - "?:"; - - const stderr = std.io.getStdErr().writer(); - var bw = std.io.bufferedWriter(stderr); - const writer = bw.writer(); - - std.debug.lockStdErr(); - defer std.debug.unlockStdErr(); - nosuspend { - writer.print("vk-{s}{s} {s}\n", .{ severity_prefix, type_prefix, message }) catch return vk.FALSE; - bw.flush() catch return vk.FALSE; - } - - return vk.FALSE; -} - -extern fn glfwGetInstanceProcAddress(instance: vk.Instance, procname: [*:0]const u8) vk.PfnVoidFunction; - -extern fn glfwGetRequiredInstanceExtensions(count: *u32) [*]const [*:0]const u8; - -extern fn glfwCreateWindowSurface( - instance: vk.Instance, - window: *Window.c.GLFWwindow, - allocation_callbacks: ?*const vk.AllocationCallbacks, - surface: *vk.SurfaceKHR, -) vk.Result; diff --git a/src/nu/Render/Context.zig b/src/nu/Render/Context.zig new file mode 100644 index 0000000..5c32c7a --- /dev/null +++ b/src/nu/Render/Context.zig @@ -0,0 +1,388 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const vk = @import("vk"); + +const nu = @import("../../nu.zig"); +const Window = @import("../Window.zig"); + +pub const use_debug_messenger = switch (builtin.mode) { + .Debug, .ReleaseSafe => true, + .ReleaseSmall, .ReleaseFast => false, +}; + +pub const apis: []const vk.ApiInfo = &.{ + vk.features.version_1_0, + vk.features.version_1_1, + vk.features.version_1_2, + vk.features.version_1_3, + vk.extensions.khr_surface, + vk.extensions.khr_swapchain, + vk.extensions.khr_dynamic_rendering, + if (use_debug_messenger) vk.extensions.ext_debug_utils else .{}, +}; + +pub const device_extensions: []const [*:0]const u8 = &.{ + // todo somehow sync this with APIs above? + vk.extensions.khr_swapchain.name, + vk.extensions.khr_dynamic_rendering.name, +}; + +pub const app_info: vk.ApplicationInfo = .{ + .p_application_name = nu.options.render.app_name, + .application_version = vk.makeApiVersion( + nu.options.render.app_version.variant, + nu.options.render.app_version.major, + nu.options.render.app_version.minor, + nu.options.render.app_version.patch, + ), + .p_engine_name = nu.options.render.engine_name, + .engine_version = vk.makeApiVersion( + nu.options.render.engine_version.variant, + nu.options.render.engine_version.major, + nu.options.render.engine_version.minor, + nu.options.render.engine_version.patch, + ), + .api_version = vk.API_VERSION_1_3, +}; + +pub const BaseWrapper = vk.BaseWrapper(apis); +pub const InstanceWrapper = vk.InstanceWrapper(apis); +pub const DeviceWrapper = vk.DeviceWrapper(apis); + +pub const InstanceProxy = vk.InstanceProxy(apis); +pub const DeviceProxy = vk.DeviceProxy(apis); +pub const QueueProxy = vk.QueueProxy(apis); +pub const CommandBufferProxy = vk.CommandBufferProxy(apis); + +pub const B: *const BaseWrapper = &_bw; +pub const I: *const InstanceProxy = &_ip; +pub const D: *const DeviceProxy = &_dp; +pub const Q: *const QueueProxy = &_qp; + +pub const device_config: *const CandidateDeviceInfo = &_dconfig; + +var _bw: BaseWrapper = undefined; +var _iw: InstanceWrapper = undefined; +var _dw: DeviceWrapper = undefined; + +var _ip: InstanceProxy = undefined; +var _dp: DeviceProxy = undefined; +var _qp: QueueProxy = undefined; + +var _instance: vk.Instance = undefined; +var _device: vk.Device = undefined; +var _dconfig: CandidateDeviceInfo = undefined; +var _queue: vk.Queue = undefined; +var _surface: vk.SurfaceKHR = undefined; + +pub fn init( + alloc: std.mem.Allocator, +) !void { + try init_base(); + errdefer deinit_base(); + + try init_instance(alloc); + errdefer deinit_instance(); + + try init_device(alloc); + errdefer deinit_device(); +} + +pub fn deinit() void { + deinit_device(); + deinit_instance(); + deinit_base(); +} + +fn init_base() !void { + if (use_debug_messenger) { + _bw = try BaseWrapper.load(glfwGetInstanceProcAddress); + } else { + _bw = BaseWrapper.loadNoFail(glfwGetInstanceProcAddress); + } +} + +fn deinit_base() void {} + +fn init_instance(alloc: std.mem.Allocator) !void { + var extensions = std.ArrayList([*:0]const u8).init(alloc); + defer extensions.deinit(); + + var layers = std.ArrayList([*:0]const u8).init(alloc); + defer layers.deinit(); + + if (use_debug_messenger) { + try extensions.appendSlice(&.{ + vk.extensions.ext_debug_utils.name, + }); + + try layers.appendSlice(&.{ + "VK_LAYER_KHRONOS_validation", + }); + } + + var glfw_exts_count: u32 = 0; + const glfw_exts: [*]const [*:0]const u8 = + @ptrCast(glfwGetRequiredInstanceExtensions(&glfw_exts_count)); + try extensions.appendSlice(glfw_exts[0..glfw_exts_count]); + + const mci: vk.DebugUtilsMessengerCreateInfoEXT = .{ + .message_severity = .{ + .error_bit_ext = true, + .info_bit_ext = true, + .verbose_bit_ext = true, + .warning_bit_ext = true, + }, + .message_type = .{ + .device_address_binding_bit_ext = true, + .general_bit_ext = false, + .performance_bit_ext = true, + .validation_bit_ext = true, + }, + .pfn_user_callback = &debug_callback, + .p_user_data = null, + }; + + _instance = try B.createInstance(&.{ + .p_application_info = &.{ + .p_application_name = "zig-glfw-vulkan", // todo RenderOptions + .application_version = vk.makeApiVersion(0, 0, 0, 0), + .p_engine_name = "nu-au", // todo RenderOptions + .engine_version = vk.makeApiVersion(0, 0, 0, 0), + .api_version = vk.API_VERSION_1_3, + }, + .enabled_extension_count = @intCast(extensions.items.len), + .pp_enabled_extension_names = extensions.items.ptr, + .enabled_layer_count = @intCast(layers.items.len), + .pp_enabled_layer_names = layers.items.ptr, + .p_next = if (use_debug_messenger) &mci else null, + }, null); + + if (use_debug_messenger) { + _iw = try InstanceWrapper.load(_instance, _bw.dispatch.vkGetInstanceProcAddr); + } else { + _iw = InstanceWrapper.loadNoFail(_instance, _bw.dispatch.vkGetInstanceProcAddr); + } + + _ip = InstanceProxy.init(_instance, &_iw); + + if (glfwCreateWindowSurface(_instance, Window.handle, null, &_surface) != .success) { + return error.glfwCreateWindowSurfaceFailed; + } +} + +fn deinit_instance() void { + _ip.destroySurfaceKHR(_surface, null); + _ip.destroyInstance(null); +} + +const CandidateDeviceInfo = struct { + pdev: vk.PhysicalDevice, + format: vk.SurfaceFormatKHR, + mode: vk.PresentModeKHR, + family: u32, // must support graphics and present for now + + fn init(alloc: std.mem.Allocator, pdev: vk.PhysicalDevice) !struct { i32, CandidateDeviceInfo } { + var score: i32 = 0; + var res: CandidateDeviceInfo = undefined; + + res.pdev = pdev; + + const props = I.getPhysicalDeviceProperties(pdev); + score += switch (props.device_type) { + vk.PhysicalDeviceType.discrete_gpu => 1000, + vk.PhysicalDeviceType.integrated_gpu => 500, + else => 0, + }; + + var format_count: u32 = undefined; + _ = try I.getPhysicalDeviceSurfaceFormatsKHR(pdev, _surface, &format_count, null); + if (format_count == 0) return error.NoSurfaceFormats; + const formats = try alloc.alloc(vk.SurfaceFormatKHR, format_count); + defer alloc.free(formats); + _ = try I.getPhysicalDeviceSurfaceFormatsKHR(pdev, _surface, &format_count, formats.ptr); + + for (formats) |fmt| { + if (fmt.color_space == .srgb_nonlinear_khr) { + res.format = fmt; + break; + } + } else { + res.format = formats[0]; + score -= 100; + } + + var mode_count: u32 = undefined; + _ = try I.getPhysicalDeviceSurfacePresentModesKHR(pdev, _surface, &mode_count, null); + if (mode_count == 0) return error.NoSurfacePresentModes; + const modes = try alloc.alloc(vk.PresentModeKHR, mode_count); + defer alloc.free(modes); + _ = try I.getPhysicalDeviceSurfacePresentModesKHR(pdev, _surface, &mode_count, modes.ptr); + + if (std.mem.indexOfAny(vk.PresentModeKHR, modes, &.{ + vk.PresentModeKHR.mailbox_khr, + vk.PresentModeKHR.immediate_khr, + })) |idx| { + res.mode = modes[idx]; + } else { + score -= 50; + res.mode = .fifo_khr; // this is guaranteed + } + + var ext_count: u32 = undefined; + _ = try I.enumerateDeviceExtensionProperties(pdev, null, &ext_count, null); + const exts = try alloc.alloc(vk.ExtensionProperties, ext_count); + defer alloc.free(exts); + _ = try I.enumerateDeviceExtensionProperties(pdev, null, &ext_count, exts.ptr); + + for (device_extensions) |needle| { + for (exts) |ext| { + if (std.mem.eql( + u8, + std.mem.span(needle), + std.mem.sliceTo(&ext.extension_name, 0), + )) + break; + } else { + return error.MissingDeviceExtension; + } + } + + var family_count: u32 = undefined; + I.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null); + const families = try alloc.alloc(vk.QueueFamilyProperties, family_count); + defer alloc.free(families); + I.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr); + + for (families, 0..) |prop, idx| { + const graphics_support = prop.queue_flags.graphics_bit; + const present_support = try I.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(idx), _surface) == vk.TRUE; + + if (graphics_support and present_support) { + res.family = @intCast(idx); + break; + } + } else { + return error.NoSuitableFamily; + } + + return .{ score, res }; + } +}; + +fn init_device(alloc: std.mem.Allocator) !void { + var pdev_count: u32 = undefined; + _ = try I.enumeratePhysicalDevices(&pdev_count, null); + if (pdev_count == 0) return error.NoDevice; + const pdevs = try alloc.alloc(vk.PhysicalDevice, pdev_count); + defer alloc.free(pdevs); + _ = try I.enumeratePhysicalDevices(&pdev_count, pdevs.ptr); + + // const scores = std.ArrayList(i32). + var scores: std.MultiArrayList(struct { score: i32, ci: CandidateDeviceInfo }) = .{}; + defer scores.deinit(alloc); + + for (pdevs) |pdev| { + const score, const ci = CandidateDeviceInfo.init(alloc, pdev) catch continue; + try scores.append(alloc, .{ .score = score, .ci = ci }); + } + + const idx = std.sort.argMax(i32, scores.items(.score), {}, std.sort.asc(i32)) orelse + return error.NoSuitableDevice; + _dconfig = scores.get(idx).ci; + + const qci: []const vk.DeviceQueueCreateInfo = &.{ + vk.DeviceQueueCreateInfo{ + .queue_family_index = _dconfig.family, + .queue_count = 1, + .p_queue_priorities = &[_]f32{1.0}, + }, + }; + + _device = try I.createDevice(_dconfig.pdev, &.{ + .queue_create_info_count = @intCast(qci.len), + .p_queue_create_infos = qci.ptr, + .enabled_extension_count = @intCast(device_extensions.len), + .pp_enabled_extension_names = device_extensions.ptr, + .p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{ + .dynamic_rendering = vk.TRUE, + }, + }, null); + + if (use_debug_messenger) { + _dw = try DeviceWrapper.load(_device, _iw.dispatch.vkGetDeviceProcAddr); + } else { + _dw = DeviceWrapper.loadNoFail(_device, _iw.dispatch.vkGetDeviceProcAddr); + } + _dp = DeviceProxy.init(_device, &_dw); + errdefer D.destroyDevice(null); + + _queue = D.getDeviceQueue(_dconfig.family, 0); + + _qp = QueueProxy.init(_queue, &_dw); + + // todo i'm thinking this needs to be a more complex pointer structure... i'm making assumptions here about how the + // command pools are meant to work. probably I am cooking too much. +} + +fn deinit_device() void { + D.destroyDevice(null); +} + +pub fn debug_callback( + msg_severity: vk.DebugUtilsMessageSeverityFlagsEXT, + msg_type: vk.DebugUtilsMessageTypeFlagsEXT, + p_data: ?*const vk.DebugUtilsMessengerCallbackDataEXT, + _: ?*anyopaque, +) callconv(vk.vulkan_call_conv) vk.Bool32 { + // ripped from std.log.defaultLog + + const data = p_data orelse return vk.FALSE; + const message = data.p_message orelse return vk.FALSE; + + const severity_prefix = if (msg_severity.verbose_bit_ext) + "verbose:" + else if (msg_severity.info_bit_ext) + "info:" + else if (msg_severity.warning_bit_ext) + "warning:" + else if (msg_severity.error_bit_ext) + "error:" + else + "?:"; + + const type_prefix = if (msg_type.general_bit_ext) + "" + else if (msg_type.validation_bit_ext) + "validation:" + else if (msg_type.performance_bit_ext) + "performance:" + else if (msg_type.device_address_binding_bit_ext) + "device_address_binding:" + else + "?:"; + + const stderr = std.io.getStdErr().writer(); + var bw = std.io.bufferedWriter(stderr); + const writer = bw.writer(); + + std.debug.lockStdErr(); + defer std.debug.unlockStdErr(); + nosuspend { + writer.print("vk-{s}{s} {s}\n", .{ severity_prefix, type_prefix, message }) catch return vk.FALSE; + bw.flush() catch return vk.FALSE; + } + + return vk.FALSE; +} + +extern fn glfwGetInstanceProcAddress(instance: vk.Instance, procname: [*:0]const u8) vk.PfnVoidFunction; + +extern fn glfwGetRequiredInstanceExtensions(count: *u32) [*]const [*:0]const u8; + +extern fn glfwCreateWindowSurface( + instance: vk.Instance, + window: *Window.c.GLFWwindow, + allocation_callbacks: ?*const vk.AllocationCallbacks, + surface: *vk.SurfaceKHR, +) vk.Result; diff --git a/src/nu/Window.zig b/src/nu/Window.zig index 1701c83..e2363ff 100644 --- a/src/nu/Window.zig +++ b/src/nu/Window.zig @@ -4,6 +4,8 @@ const std = @import("std"); +const nu = @import("../nu.zig"); + pub const c = @cImport({ @cDefine("GLFW_INCLUDE_NONE", {}); @cInclude("GLFW/glfw3.h"); @@ -12,7 +14,7 @@ pub const c = @cImport({ pub const Bus = @import("Bus.zig"); pub const Options = struct { - title: [*:0]const u8, + title: [*:0]const u8 = "Hello World", width: u32 = 1280, height: u32 = 720, x11_class_name: [*:0]const u8 = "floating_window", @@ -23,7 +25,7 @@ var bus: Bus = undefined; pub var handle: *c.GLFWwindow = undefined; var unfocused_rate: f32 = 1.0 / 20.0; -pub fn init(alloc: std.mem.Allocator, options: Options) !void { +pub fn init(alloc: std.mem.Allocator) !void { if (c.glfwInit() != c.GLFW_TRUE) return error.glfwInitFailed; errdefer c.glfwTerminate(); @@ -32,13 +34,13 @@ pub fn init(alloc: std.mem.Allocator, options: Options) !void { errdefer bus.deinit(); c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API); - c.glfwWindowHintString(c.GLFW_X11_CLASS_NAME, options.x11_class_name); - c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, options.x11_instance_name); + c.glfwWindowHintString(c.GLFW_X11_CLASS_NAME, nu.options.window.x11_class_name); + c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, nu.options.window.x11_instance_name); handle = c.glfwCreateWindow( - @intCast(options.width), - @intCast(options.height), - options.title, + @intCast(nu.options.window.width), + @intCast(nu.options.window.height), + nu.options.window.title, null, null, ) orelse