root module nu_options
This commit is contained in:
11
src/main.zig
11
src/main.zig
@@ -1,9 +1,13 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
const nu = @import("nu.zig");
|
const nu = @import("nu.zig");
|
||||||
|
|
||||||
const App = @import("App.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 {
|
pub fn main() !void {
|
||||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||||
defer _ = gpa.detectLeaks();
|
defer _ = gpa.detectLeaks();
|
||||||
@@ -13,7 +17,10 @@ pub fn main() !void {
|
|||||||
// problem: how to specify runtime options, like Window title?
|
// problem: how to specify runtime options, like Window title?
|
||||||
// problem: where should gpa go? probably some "Engine" structure in nu.zig
|
// 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();
|
defer nu.Window.deinit();
|
||||||
|
|
||||||
try nu.Render.init(alloc);
|
try nu.Render.init(alloc);
|
||||||
|
@@ -1,9 +1,18 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const root = @import("root");
|
||||||
|
|
||||||
pub const Window = @import("nu/Window.zig");
|
pub const Window = @import("nu/Window.zig");
|
||||||
pub const Render = @import("nu/Render.zig");
|
pub const Render = @import("nu/Render.zig");
|
||||||
pub const ImGui = @import("nu/ImGui.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(
|
pub fn run(
|
||||||
driver: anytype,
|
driver: anytype,
|
||||||
modules: anytype,
|
modules: anytype,
|
||||||
|
@@ -6,378 +6,35 @@ const std = @import("std");
|
|||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const vk = @import("vk");
|
const vk = @import("vk");
|
||||||
|
|
||||||
const Window = @import("Window.zig");
|
const ctx = @import("Render/Context.zig");
|
||||||
|
|
||||||
pub const RenderOptions = struct {
|
pub const Options = struct {
|
||||||
app_name: []const u8,
|
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_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) {
|
pub fn init(alloc: std.mem.Allocator) !void {
|
||||||
.Debug, .ReleaseSafe => true,
|
// todo make ctx not globals
|
||||||
.ReleaseSmall, .ReleaseFast => false,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub const apis: []const vk.ApiInfo = &.{
|
try ctx.init(alloc);
|
||||||
vk.features.version_1_0,
|
errdefer ctx.deinit();
|
||||||
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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn frame() void {}
|
||||||
|
|
||||||
pub fn deinit() void {
|
pub fn deinit() void {
|
||||||
deinit_device();
|
ctx.deinit();
|
||||||
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;
|
|
||||||
|
388
src/nu/Render/Context.zig
Normal file
388
src/nu/Render/Context.zig
Normal file
@@ -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;
|
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
|
||||||
|
const nu = @import("../nu.zig");
|
||||||
|
|
||||||
pub const c = @cImport({
|
pub const c = @cImport({
|
||||||
@cDefine("GLFW_INCLUDE_NONE", {});
|
@cDefine("GLFW_INCLUDE_NONE", {});
|
||||||
@cInclude("GLFW/glfw3.h");
|
@cInclude("GLFW/glfw3.h");
|
||||||
@@ -12,7 +14,7 @@ pub const c = @cImport({
|
|||||||
pub const Bus = @import("Bus.zig");
|
pub const Bus = @import("Bus.zig");
|
||||||
|
|
||||||
pub const Options = struct {
|
pub const Options = struct {
|
||||||
title: [*:0]const u8,
|
title: [*:0]const u8 = "Hello World",
|
||||||
width: u32 = 1280,
|
width: u32 = 1280,
|
||||||
height: u32 = 720,
|
height: u32 = 720,
|
||||||
x11_class_name: [*:0]const u8 = "floating_window",
|
x11_class_name: [*:0]const u8 = "floating_window",
|
||||||
@@ -23,7 +25,7 @@ var bus: Bus = undefined;
|
|||||||
pub var handle: *c.GLFWwindow = undefined;
|
pub var handle: *c.GLFWwindow = undefined;
|
||||||
var unfocused_rate: f32 = 1.0 / 20.0;
|
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)
|
if (c.glfwInit() != c.GLFW_TRUE)
|
||||||
return error.glfwInitFailed;
|
return error.glfwInitFailed;
|
||||||
errdefer c.glfwTerminate();
|
errdefer c.glfwTerminate();
|
||||||
@@ -32,13 +34,13 @@ pub fn init(alloc: std.mem.Allocator, options: Options) !void {
|
|||||||
errdefer bus.deinit();
|
errdefer bus.deinit();
|
||||||
|
|
||||||
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
|
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_CLASS_NAME, nu.options.window.x11_class_name);
|
||||||
c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, options.x11_instance_name);
|
c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, nu.options.window.x11_instance_name);
|
||||||
|
|
||||||
handle = c.glfwCreateWindow(
|
handle = c.glfwCreateWindow(
|
||||||
@intCast(options.width),
|
@intCast(nu.options.window.width),
|
||||||
@intCast(options.height),
|
@intCast(nu.options.window.height),
|
||||||
options.title,
|
nu.options.window.title,
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
) orelse
|
) orelse
|
||||||
|
Reference in New Issue
Block a user