move to au globals

This commit is contained in:
David Allemang
2024-06-26 12:14:36 -04:00
parent 237dc9eb7e
commit 9d99902b42
2 changed files with 531 additions and 95 deletions

432
src/au.zig Normal file
View File

@@ -0,0 +1,432 @@
const std = @import("std");
const builtin = @import("builtin");
const vk = @import("vk");
const c = @import("c.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 = "zig-glfw-vulkan",
.application_version = vk.makeApiVersion(0, 0, 0, 0),
.p_engine_name = "zig-glfw-vulkan",
.engine_version = vk.makeApiVersion(0, 0, 0, 0),
.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 W: *const Window = &_window;
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 _window: Window = undefined;
var _device: vk.Device = undefined;
var _dconfig: CandidateDeviceInfo = undefined;
var _queue: vk.Queue = undefined;
pub fn init(alloc: std.mem.Allocator) !void {
try init_glfw();
errdefer deinit_glfw();
try init_base();
errdefer deinit_base();
try init_instance(alloc);
errdefer deinit_instance();
try init_window();
errdefer deinit_window();
try init_device(alloc);
errdefer deinit_device();
}
pub fn deinit() void {
deinit_device();
deinit_window();
deinit_instance();
deinit_base();
deinit_glfw();
}
fn init_glfw() !void {
if (c.glfwInit() != c.GLFW_TRUE)
return error.glfwInitFailed;
errdefer c.glfwTerminate();
if (c.glfwVulkanSupported() != c.GLFW_TRUE)
return error.glfwNoVulkan;
}
fn deinit_glfw() void {
c.glfwTerminate();
}
fn init_base() !void {
if (use_debug_messenger) {
_bw = try BaseWrapper.load(c.glfwGetInstanceProcAddress);
} else {
_bw = BaseWrapper.loadNoFail(c.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(c.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 = &app_info,
.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);
}
fn deinit_instance() void {
_ip.destroyInstance(null);
}
fn init_window() !void {
_window = try Window.init(
app_info.p_application_name orelse "Au Window",
.{ .height = 720, .width = 1280 },
);
errdefer _window.deinit();
}
fn deinit_window() void {
_window.deinit();
}
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;
var format_count: u32 = undefined;
_ = try I.getPhysicalDeviceSurfaceFormatsKHR(pdev, W.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, W.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, W.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, W.surface, &mode_count, modes.ptr);
if (std.mem.indexOfAny(vk.PresentModeKHR, modes, &.{
vk.PresentModeKHR.mailbox_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), W.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;
}
const Window = struct {
const Self = @This();
handle: *c.GLFWwindow,
surface: vk.SurfaceKHR,
pub fn init(title: [*:0]const u8, extent: vk.Extent2D) !Self {
var self: Self = undefined;
c.glfwWindowHintString(c.GLFW_X11_CLASS_NAME, "floating_window");
c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, "floating_window");
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
self.handle = c.glfwCreateWindow(
@intCast(extent.width),
@intCast(extent.height),
title,
null,
null,
) orelse return error.glfwWindowFailed;
errdefer c.glfwDestroyWindow(self.handle);
if (c.glfwCreateWindowSurface(_instance, self.handle, null, &self.surface) != .success) {
return error.glfwSurfaceFailed;
}
errdefer I.destroySurfaceKHR(self.surface, null);
return self;
}
pub fn deinit(self: Self) void {
I.destroySurfaceKHR(self.surface, null);
c.glfwDestroyWindow(self.handle);
}
pub fn should_close(self: Self) bool {
return c.glfwWindowShouldClose(self.handle) == c.GLFW_TRUE;
}
pub fn wait_events(_: Self) void {
c.glfwWaitEvents();
// todo events as values? push into a buffer and return here?
}
pub fn poll_events(_: Self) void {
c.glfwPollEvents();
}
pub fn wait_events_timeout(seconds: f64) void {
c.glfwWaitEventsTimeout(seconds);
}
};

View File

@@ -6,6 +6,8 @@ const Allocator = std.mem.Allocator;
const gfx = @import("gfx.zig"); const gfx = @import("gfx.zig");
const au = @import("au.zig");
const app_name = "vulkan-zig triangle example"; const app_name = "vulkan-zig triangle example";
const Vertex = extern struct { const Vertex = extern struct {
@@ -94,106 +96,108 @@ pub fn main() !void {
defer _ = gpa.deinit(); defer _ = gpa.deinit();
const ally = gpa.allocator(); const ally = gpa.allocator();
const base = try gfx.Base.init(); try au.init(ally);
defer base.deinit(); defer au.deinit();
const inst = try gfx.Instance.init(&base); // std.debug.print("Initialized!!\n", .{ });
defer inst.deinit();
const win = try gfx.Window.init(&inst, "zig-glfw-vulkan", .{ .width = 800, .height = 600 }); while (!au.W.should_close()) {
defer win.deinit(); au.W.wait_events();
// std.debug.print("Event!!\n", .{ });
const dev = try gfx.Device.init(ally, &inst, &win);
defer dev.deinit();
var sc = try gfx.Swapchain.create(ally, &dev);
defer sc.deinit();
const device_local = gfx.VkAllocator.init(dev.pdev, inst.vki);
const pipeline_layout = try dev.vkd.createPipelineLayout(dev.dev, &.{
.flags = .{},
.set_layout_count = 0,
.p_set_layouts = undefined,
.push_constant_range_count = 0,
.p_push_constant_ranges = undefined,
}, null);
defer dev.vkd.destroyPipelineLayout(dev.dev, pipeline_layout, null);
const pipeline = try createPipeline(dev.dev, pipeline_layout, dev.format, dev.vkd);
defer dev.vkd.destroyPipeline(dev.dev, pipeline, null);
const vertex_buffer = try dev.vkd.createBuffer(dev.dev, &.{
.size = @sizeOf(@TypeOf(vertices)),
.usage = .{ .transfer_dst_bit = true, .vertex_buffer_bit = true },
.sharing_mode = .exclusive,
}, null);
defer dev.vkd.destroyBuffer(dev.dev, vertex_buffer, null);
const vertex_mem_reqs = dev.vkd.getBufferMemoryRequirements(dev.dev, vertex_buffer);
const vertex_memory = try device_local.alloc(dev.dev, dev.vkd, vertex_mem_reqs, .{ .device_local_bit = true });
defer dev.vkd.freeMemory(dev.dev, vertex_memory, null);
try dev.vkd.bindBufferMemory(dev.dev, vertex_buffer, vertex_memory, 0);
try gfx.uploadData(Vertex, dev.pdev, inst.vki, dev.dev, dev.vkd, dev.queue, dev.pool, vertex_buffer, &vertices);
const index_buffer = try dev.vkd.createBuffer(dev.dev, &.{
.size = @sizeOf(@TypeOf(indices)),
.usage = .{ .transfer_dst_bit = true, .index_buffer_bit = true },
.sharing_mode = .exclusive,
}, null);
defer dev.vkd.destroyBuffer(dev.dev, index_buffer, null);
const index_mem_reqs = dev.vkd.getBufferMemoryRequirements(dev.dev, index_buffer);
const index_memory = try device_local.alloc(dev.dev, dev.vkd, index_mem_reqs, .{ .device_local_bit = true });
defer dev.vkd.freeMemory(dev.dev, index_memory, null);
try dev.vkd.bindBufferMemory(dev.dev, index_buffer, index_memory, 0);
try gfx.uploadData(Index, dev.pdev, inst.vki, dev.dev, dev.vkd, dev.queue, dev.pool, index_buffer, &indices);
try sc.init();
for (sc.chain.items(.image), sc.chain.items(.view), sc.chain.items(.cmdbuf)) |image, view, cmdbuf| {
try record_cmdbuf(cmdbuf, dev.vkd, image, view, sc.extent, pipeline, vertex_buffer, index_buffer);
} }
var index: u32 = 0; try au.D.deviceWaitIdle();
while (c.glfwWindowShouldClose(win.ref) == c.GLFW_FALSE) { // // todo create command pool
var w: c_int = undefined; //
var h: c_int = undefined; // var sc = try gfx.Swapchain.create(ally, &dev);
c.glfwGetFramebufferSize(win.ref, &w, &h); // defer sc.deinit();
//
// Don't present or resize swapchain while the window is minimized // const device_local = gfx.VkAllocator.init(dev.pdev, inst.vki);
if (w == 0 or h == 0) { //
c.glfwPollEvents(); // const pipeline_layout = try dev.vkd.createPipelineLayout(dev.dev, &.{
continue; // .flags = .{},
} // .set_layout_count = 0,
// .p_set_layouts = undefined,
const frame = sc.chain.get(index); // .push_constant_range_count = 0,
// .p_push_constant_ranges = undefined,
render(dev.dev, dev.vkd, sc.ref, frame, dev.queue) catch |err| switch (err) { // }, null);
error.OutOfDateKHR => { // defer dev.vkd.destroyPipelineLayout(dev.dev, pipeline_layout, null);
// TODO: this is a hack to safely destroy sync primitives //
// don't do this. be smart about sync primitive reuse or // const pipeline = try createPipeline(dev.dev, pipeline_layout, dev.format, dev.vkd);
// move them to "garbage" to be destroyed later. // defer dev.vkd.destroyPipeline(dev.dev, pipeline, null);
try dev.vkd.deviceWaitIdle(dev.dev); //
// const vertex_buffer = try dev.vkd.createBuffer(dev.dev, &.{
try sc.init(); // .size = @sizeOf(@TypeOf(vertices)),
for (sc.chain.items(.image), sc.chain.items(.view), sc.chain.items(.cmdbuf)) |image, view, cmdbuf| { // .usage = .{ .transfer_dst_bit = true, .vertex_buffer_bit = true },
try record_cmdbuf(cmdbuf, dev.vkd, image, view, sc.extent, pipeline, vertex_buffer, index_buffer); // .sharing_mode = .exclusive,
} // }, null);
// defer dev.vkd.destroyBuffer(dev.dev, vertex_buffer, null);
index = 0; // const vertex_mem_reqs = dev.vkd.getBufferMemoryRequirements(dev.dev, vertex_buffer);
// const vertex_memory = try device_local.alloc(dev.dev, dev.vkd, vertex_mem_reqs, .{ .device_local_bit = true });
continue; // defer dev.vkd.freeMemory(dev.dev, vertex_memory, null);
}, // try dev.vkd.bindBufferMemory(dev.dev, vertex_buffer, vertex_memory, 0);
else => |errx| return errx, //
}; // try gfx.uploadData(Vertex, dev.pdev, inst.vki, dev.dev, dev.vkd, dev.queue, dev.pool, vertex_buffer, &vertices);
//
c.glfwPollEvents(); // const index_buffer = try dev.vkd.createBuffer(dev.dev, &.{
// .size = @sizeOf(@TypeOf(indices)),
index = @intCast((index + 1) % sc.chain.len); // .usage = .{ .transfer_dst_bit = true, .index_buffer_bit = true },
} // .sharing_mode = .exclusive,
// }, null);
try dev.vkd.deviceWaitIdle(dev.dev); // defer dev.vkd.destroyBuffer(dev.dev, index_buffer, null);
// const index_mem_reqs = dev.vkd.getBufferMemoryRequirements(dev.dev, index_buffer);
// const index_memory = try device_local.alloc(dev.dev, dev.vkd, index_mem_reqs, .{ .device_local_bit = true });
// defer dev.vkd.freeMemory(dev.dev, index_memory, null);
// try dev.vkd.bindBufferMemory(dev.dev, index_buffer, index_memory, 0);
//
// try gfx.uploadData(Index, dev.pdev, inst.vki, dev.dev, dev.vkd, dev.queue, dev.pool, index_buffer, &indices);
//
// try sc.init();
// for (sc.chain.items(.image), sc.chain.items(.view), sc.chain.items(.cmdbuf)) |image, view, cmdbuf| {
// try record_cmdbuf(cmdbuf, dev.vkd, image, view, sc.extent, pipeline, vertex_buffer, index_buffer);
// }
//
// var index: u32 = 0;
//
// while (c.glfwWindowShouldClose(win.ref) == c.GLFW_FALSE) {
// var w: c_int = undefined;
// var h: c_int = undefined;
// c.glfwGetFramebufferSize(win.ref, &w, &h);
//
// // Don't present or resize swapchain while the window is minimized
// if (w == 0 or h == 0) {
// c.glfwPollEvents();
// continue;
// }
//
// const frame = sc.chain.get(index);
//
// render(dev.dev, dev.vkd, sc.ref, frame, dev.queue) catch |err| switch (err) {
// error.OutOfDateKHR => {
// // TODO: this is a hack to safely destroy sync primitives
// // don't do this. be smart about sync primitive reuse or
// // move them to "garbage" to be destroyed later.
// try dev.vkd.deviceWaitIdle(dev.dev);
//
// try sc.init();
// for (sc.chain.items(.image), sc.chain.items(.view), sc.chain.items(.cmdbuf)) |image, view, cmdbuf| {
// try record_cmdbuf(cmdbuf, dev.vkd, image, view, sc.extent, pipeline, vertex_buffer, index_buffer);
// }
//
// index = 0;
//
// continue;
// },
// else => |errx| return errx,
// };
//
// c.glfwPollEvents();
//
// index = @intCast((index + 1) % sc.chain.len);
// }
//
// try dev.vkd.deviceWaitIdle(dev.dev);
} }
fn record_cmdbuf( fn record_cmdbuf(