MAJOR restructure
This commit is contained in:
686
src/gfx.zig
686
src/gfx.zig
@@ -4,282 +4,282 @@ 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,
|
||||
};
|
||||
const Base = @import("gfx/Base.zig");
|
||||
const Instance = @import("gfx/Instance.zig");
|
||||
const Device = @import("gfx/Device.zig");
|
||||
|
||||
const InstancePair = std.meta.Tuple(&.{ vk.Instance, InstanceDispatch, vk.DebugUtilsMessengerEXT });
|
||||
// const InstancePair = std.meta.Tuple(&.{ vk.Instance, InstanceDispatch, vk.DebugUtilsMessengerEXT });
|
||||
|
||||
/// note: destroy with vki.destroyInstance(instance, null)
|
||||
pub fn create_instance(vkb: BaseDispatch, app_name: [*:0]const u8) !InstancePair {
|
||||
var exts = std.BoundedArray([*:0]const u8, 32){};
|
||||
var layers = std.BoundedArray([*:0]const u8, 32){};
|
||||
// /// note: destroy with vki.destroyInstance(instance, null)
|
||||
// pub fn create_instance(vkb: BaseDispatch, app_name: [*:0]const u8) !InstancePair {
|
||||
// var exts = std.BoundedArray([*:0]const u8, 32){};
|
||||
// var layers = std.BoundedArray([*:0]const u8, 32){};
|
||||
//
|
||||
// if (use_debug_messenger) {
|
||||
// try exts.appendSlice(&.{
|
||||
// vk.extension_info.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 exts.appendSlice(glfw_exts[0..glfw_exts_count]);
|
||||
//
|
||||
// const dumci: 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,
|
||||
// };
|
||||
//
|
||||
// const instance = try vkb.createInstance(&vk.InstanceCreateInfo{
|
||||
// .p_application_info = &vk.ApplicationInfo{
|
||||
// .p_application_name = app_name,
|
||||
// .application_version = vk.makeApiVersion(0, 0, 0, 0),
|
||||
// .p_engine_name = app_name,
|
||||
// .engine_version = vk.makeApiVersion(0, 0, 0, 0),
|
||||
// .api_version = vk.API_VERSION_1_3,
|
||||
// },
|
||||
// .enabled_extension_count = @intCast(exts.len),
|
||||
// .pp_enabled_extension_names = &exts.buffer,
|
||||
// .enabled_layer_count = @intCast(layers.len),
|
||||
// .pp_enabled_layer_names = &layers.buffer,
|
||||
// .p_next = if (use_debug_messenger) &dumci else null,
|
||||
// }, null);
|
||||
//
|
||||
// const vki = try InstanceDispatch.load(instance, vkb.dispatch.vkGetInstanceProcAddr);
|
||||
// errdefer vki.destroyInstance(instance, null);
|
||||
//
|
||||
// const messenger: vk.DebugUtilsMessengerEXT = if (use_debug_messenger)
|
||||
// try vki.createDebugUtilsMessengerEXT(instance, &dumci, null)
|
||||
// else
|
||||
// .null_handle;
|
||||
// errdefer if (use_debug_messenger)
|
||||
// vki.destroyDebugUtilsMessengerEXT(instance, messenger, null);
|
||||
//
|
||||
// return .{ instance, vki, messenger };
|
||||
// }
|
||||
|
||||
if (use_debug_messenger) {
|
||||
try exts.appendSlice(&.{
|
||||
vk.extension_info.ext_debug_utils.name,
|
||||
});
|
||||
// /// note: destroy with vki.destroySurfaceKHR(instance, surface, null)
|
||||
// pub fn create_surface(instance: vk.Instance, window: *c.GLFWwindow) !vk.SurfaceKHR {
|
||||
// var surface: vk.SurfaceKHR = undefined;
|
||||
// if (c.glfwCreateWindowSurface(instance, window, null, &surface) != .success) {
|
||||
// return error.SurfaceInitFailed;
|
||||
// }
|
||||
// return surface;
|
||||
// }
|
||||
|
||||
try layers.appendSlice(&.{
|
||||
"VK_LAYER_KHRONOS_validation",
|
||||
});
|
||||
}
|
||||
// /// note: destroy with c.glfwDestroyWindow(window)
|
||||
// pub fn create_window(extent: vk.Extent2D, title: [*:0]const u8) !*c.GLFWwindow {
|
||||
// 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);
|
||||
//
|
||||
// return c.glfwCreateWindow(
|
||||
// @intCast(extent.width),
|
||||
// @intCast(extent.height),
|
||||
// title,
|
||||
// null,
|
||||
// null,
|
||||
// ) orelse error.WindowInitFailed;
|
||||
// }
|
||||
|
||||
var glfw_exts_count: u32 = 0;
|
||||
const glfw_exts: [*]const [*:0]const u8 =
|
||||
@ptrCast(c.glfwGetRequiredInstanceExtensions(&glfw_exts_count));
|
||||
try exts.appendSlice(glfw_exts[0..glfw_exts_count]);
|
||||
|
||||
const dumci: 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,
|
||||
};
|
||||
|
||||
const instance = try vkb.createInstance(&vk.InstanceCreateInfo{
|
||||
.p_application_info = &vk.ApplicationInfo{
|
||||
.p_application_name = app_name,
|
||||
.application_version = vk.makeApiVersion(0, 0, 0, 0),
|
||||
.p_engine_name = app_name,
|
||||
.engine_version = vk.makeApiVersion(0, 0, 0, 0),
|
||||
.api_version = vk.API_VERSION_1_3,
|
||||
},
|
||||
.enabled_extension_count = @intCast(exts.len),
|
||||
.pp_enabled_extension_names = &exts.buffer,
|
||||
.enabled_layer_count = @intCast(layers.len),
|
||||
.pp_enabled_layer_names = &layers.buffer,
|
||||
.p_next = if (use_debug_messenger) &dumci else null,
|
||||
}, null);
|
||||
const vki = try InstanceDispatch.load(instance, vkb.dispatch.vkGetInstanceProcAddr);
|
||||
errdefer vki.destroyInstance(instance, null);
|
||||
|
||||
const messenger: vk.DebugUtilsMessengerEXT = if (use_debug_messenger)
|
||||
try vki.createDebugUtilsMessengerEXT(instance, &dumci, null)
|
||||
else
|
||||
.null_handle;
|
||||
errdefer if (use_debug_messenger)
|
||||
vki.destroyDebugUtilsMessengerEXT(instance, messenger, null);
|
||||
|
||||
return .{ instance, vki, messenger };
|
||||
}
|
||||
|
||||
/// note: destroy with vki.destroySurfaceKHR(instance, surface, null)
|
||||
pub fn create_surface(instance: vk.Instance, window: *c.GLFWwindow) !vk.SurfaceKHR {
|
||||
var surface: vk.SurfaceKHR = undefined;
|
||||
if (c.glfwCreateWindowSurface(instance, window, null, &surface) != .success) {
|
||||
return error.SurfaceInitFailed;
|
||||
}
|
||||
return surface;
|
||||
}
|
||||
|
||||
/// note: destroy with c.glfwDestroyWindow(window)
|
||||
pub fn create_window(extent: vk.Extent2D, title: [*:0]const u8) !*c.GLFWwindow {
|
||||
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);
|
||||
|
||||
return c.glfwCreateWindow(
|
||||
@intCast(extent.width),
|
||||
@intCast(extent.height),
|
||||
title,
|
||||
null,
|
||||
null,
|
||||
) orelse error.WindowInitFailed;
|
||||
}
|
||||
|
||||
const DevicePair = std.meta.Tuple(&.{ vk.PhysicalDevice, vk.Device, DeviceDispatch, u32 });
|
||||
|
||||
/// note: destroy with vkd.destroyDevice(dev, null)
|
||||
pub fn create_device(
|
||||
ally: std.mem.Allocator,
|
||||
instance: vk.Instance,
|
||||
surface: vk.SurfaceKHR,
|
||||
vki: InstanceDispatch,
|
||||
) !DevicePair {
|
||||
const required_device_extensions: []const [*:0]const u8 = &.{
|
||||
vk.extension_info.khr_swapchain.name,
|
||||
vk.extension_info.khr_dynamic_rendering.name,
|
||||
};
|
||||
|
||||
var pdev_count: u32 = undefined;
|
||||
_ = try vki.enumeratePhysicalDevices(instance, &pdev_count, null);
|
||||
const pdevs = try ally.alloc(vk.PhysicalDevice, pdev_count);
|
||||
defer ally.free(pdevs);
|
||||
_ = try vki.enumeratePhysicalDevices(instance, &pdev_count, pdevs.ptr);
|
||||
|
||||
pdev_search: for (pdevs) |pdev| {
|
||||
const props = vki.getPhysicalDeviceProperties(pdev);
|
||||
if (props.device_type != .discrete_gpu) continue :pdev_search;
|
||||
|
||||
var format_count: u32 = undefined;
|
||||
_ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &format_count, null);
|
||||
if (format_count == 0) continue :pdev_search;
|
||||
|
||||
var mode_count: u32 = undefined;
|
||||
_ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &mode_count, null);
|
||||
if (mode_count == 0) continue :pdev_search;
|
||||
|
||||
var ext_count: u32 = undefined;
|
||||
_ = try vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, null);
|
||||
const exts = try ally.alloc(vk.ExtensionProperties, ext_count);
|
||||
defer ally.free(exts);
|
||||
_ = try vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, exts.ptr);
|
||||
|
||||
for (required_device_extensions) |name| {
|
||||
for (exts) |ext| {
|
||||
if (std.mem.eql(
|
||||
u8,
|
||||
std.mem.span(name),
|
||||
std.mem.sliceTo(&ext.extension_name, 0),
|
||||
)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
continue :pdev_search;
|
||||
}
|
||||
}
|
||||
|
||||
var family_count: u32 = undefined;
|
||||
vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null);
|
||||
const families = try ally.alloc(vk.QueueFamilyProperties, family_count);
|
||||
defer ally.free(families);
|
||||
vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr);
|
||||
|
||||
// just find one family that does graphics and present, so we can use exclusive sharing
|
||||
// on the swapchain. apparently most hardware supports this. logic for queue allocation
|
||||
// and swapchain creation is so much simpler this way. swapchain creation needs to know
|
||||
// the list of queue family indices which will have access to the images, and there's a
|
||||
// performance penalty to allow concurrent access to multiple queue families.
|
||||
//
|
||||
// multiple _queues_ may have exclusive access, but only if they're in the smae family.
|
||||
|
||||
const graphics_family: u32 = for (families, 0..) |family, idx| {
|
||||
const graphics = family.queue_flags.graphics_bit;
|
||||
const present = try vki.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(idx), surface) == vk.TRUE;
|
||||
if (graphics and present) {
|
||||
break @intCast(idx);
|
||||
}
|
||||
} else {
|
||||
continue :pdev_search;
|
||||
};
|
||||
|
||||
std.log.debug("selecting device {s}", .{std.mem.sliceTo(&props.device_name, 0)});
|
||||
|
||||
const qci: []const vk.DeviceQueueCreateInfo = &.{
|
||||
vk.DeviceQueueCreateInfo{
|
||||
.queue_family_index = graphics_family,
|
||||
.queue_count = 1,
|
||||
.p_queue_priorities = &[_]f32{1.0},
|
||||
},
|
||||
};
|
||||
|
||||
const dev = try vki.createDevice(pdev, &.{
|
||||
.queue_create_info_count = @intCast(qci.len),
|
||||
.p_queue_create_infos = qci.ptr,
|
||||
.enabled_extension_count = @intCast(required_device_extensions.len),
|
||||
.pp_enabled_extension_names = required_device_extensions.ptr,
|
||||
.p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{
|
||||
.dynamic_rendering = vk.TRUE,
|
||||
},
|
||||
}, null);
|
||||
const vkd = try DeviceDispatch.load(dev, vki.dispatch.vkGetDeviceProcAddr);
|
||||
errdefer vkd.destroyDevice(dev, null);
|
||||
|
||||
return .{ pdev, dev, vkd, graphics_family };
|
||||
}
|
||||
|
||||
return error.NoSuitableDevice;
|
||||
}
|
||||
|
||||
pub fn find_surface_format(
|
||||
pdev: vk.PhysicalDevice,
|
||||
vki: InstanceDispatch,
|
||||
surface: vk.SurfaceKHR,
|
||||
preferred: vk.SurfaceFormatKHR,
|
||||
) !vk.SurfaceFormatKHR {
|
||||
var formats_buf: [64]vk.SurfaceFormatKHR = undefined;
|
||||
var formats_count: u32 = @intCast(formats_buf.len);
|
||||
_ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &formats_count, &formats_buf);
|
||||
const formats = formats_buf[0..formats_count];
|
||||
|
||||
for (formats) |format| {
|
||||
if (std.meta.eql(format, preferred)) {
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
return formats[0];
|
||||
}
|
||||
|
||||
pub fn find_present_mode(
|
||||
pdev: vk.PhysicalDevice,
|
||||
vki: InstanceDispatch,
|
||||
surface: vk.SurfaceKHR,
|
||||
preferred: vk.PresentModeKHR,
|
||||
) !vk.PresentModeKHR {
|
||||
var modes_buf: [8]vk.PresentModeKHR = undefined;
|
||||
var modes_count: u32 = @intCast(modes_buf.len);
|
||||
_ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &modes_count, &modes_buf);
|
||||
const modes = modes_buf[0..modes_count];
|
||||
|
||||
for (modes) |mode| {
|
||||
if (std.meta.eql(mode, preferred)) {
|
||||
return mode;
|
||||
}
|
||||
}
|
||||
|
||||
return .mailbox_khr;
|
||||
}
|
||||
|
||||
pub fn find_swap_extent(
|
||||
pdev: vk.PhysicalDevice,
|
||||
vki: InstanceDispatch,
|
||||
surface: vk.SurfaceKHR,
|
||||
window: *c.GLFWwindow,
|
||||
) !vk.Extent2D {
|
||||
const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, surface);
|
||||
var extent = caps.current_extent;
|
||||
|
||||
if (extent.width == std.math.maxInt(u32)) {
|
||||
c.glfwGetFramebufferSize(window, @ptrCast(&extent.width), @ptrCast(&extent.height));
|
||||
extent.width = std.math.clamp(extent.width, caps.min_image_extent.width, caps.max_image_extent.width);
|
||||
extent.height = std.math.clamp(extent.height, caps.min_image_extent.height, caps.max_image_extent.height);
|
||||
}
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
pub fn find_swap_image_count(
|
||||
pdev: vk.PhysicalDevice,
|
||||
vki: InstanceDispatch,
|
||||
surface: vk.SurfaceKHR,
|
||||
) !u32 {
|
||||
const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, surface);
|
||||
var count = @max(3, caps.min_image_count + 1);
|
||||
if (caps.max_image_count > 0) {
|
||||
count = @min(count, caps.max_image_count);
|
||||
}
|
||||
return count;
|
||||
}
|
||||
// const DevicePair = std.meta.Tuple(&.{ vk.PhysicalDevice, vk.Device, Device.Wrapper, u32 });
|
||||
//
|
||||
// /// note: destroy with vkd.destroyDevice(dev, null)
|
||||
// pub fn cr eate_device(
|
||||
// ally: std.mem.Allocator,
|
||||
// instance: vk.Instance,
|
||||
// surface: vk.SurfaceKHR,
|
||||
// vki: InstanceDispatch,
|
||||
// ) !DevicePair {
|
||||
// const required_device_extensions: []const [*:0]const u8 = &.{
|
||||
// vk.extension_info.khr_swapchain.name,
|
||||
// vk.extension_info.khr_dynamic_rendering.name,
|
||||
// };
|
||||
//
|
||||
// var pdev_count: u32 = undefined;
|
||||
// _ = try vki.enumeratePhysicalDevices(instance, &pdev_count, null);
|
||||
// const pdevs = try ally.alloc(vk.PhysicalDevice, pdev_count);
|
||||
// defer ally.free(pdevs);
|
||||
// _ = try vki.enumeratePhysicalDevices(instance, &pdev_count, pdevs.ptr);
|
||||
//
|
||||
// pdev_search: for (pdevs) |pdev| {
|
||||
// const props = vki.getPhysicalDeviceProperties(pdev);
|
||||
// if (props.device_type != .discrete_gpu) continue :pdev_search;
|
||||
//
|
||||
// var format_count: u32 = undefined;
|
||||
// _ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &format_count, null);
|
||||
// if (format_count == 0) continue :pdev_search;
|
||||
//
|
||||
// var mode_count: u32 = undefined;
|
||||
// _ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &mode_count, null);
|
||||
// if (mode_count == 0) continue :pdev_search;
|
||||
//
|
||||
// var ext_count: u32 = undefined;
|
||||
// _ = try vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, null);
|
||||
// const exts = try ally.alloc(vk.ExtensionProperties, ext_count);
|
||||
// defer ally.free(exts);
|
||||
// _ = try vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, exts.ptr);
|
||||
//
|
||||
// for (required_device_extensions) |name| {
|
||||
// for (exts) |ext| {
|
||||
// if (std.mem.eql(
|
||||
// u8,
|
||||
// std.mem.span(name),
|
||||
// std.mem.sliceTo(&ext.extension_name, 0),
|
||||
// )) {
|
||||
// break;
|
||||
// }
|
||||
// } else {
|
||||
// continue :pdev_search;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// var family_count: u32 = undefined;
|
||||
// vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null);
|
||||
// const families = try ally.alloc(vk.QueueFamilyProperties, family_count);
|
||||
// defer ally.free(families);
|
||||
// vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr);
|
||||
//
|
||||
// // just find one family that does graphics and present, so we can use exclusive sharing
|
||||
// // on the swapchain. apparently most hardware supports this. logic for queue allocation
|
||||
// // and swapchain creation is so much simpler this way. swapchain creation needs to know
|
||||
// // the list of queue family indices which will have access to the images, and there's a
|
||||
// // performance penalty to allow concurrent access to multiple queue families.
|
||||
// //
|
||||
// // multiple _queues_ may have exclusive access, but only if they're in the smae family.
|
||||
//
|
||||
// const graphics_family: u32 = for (families, 0..) |family, idx| {
|
||||
// const graphics = family.queue_flags.graphics_bit;
|
||||
// const present = try vki.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(idx), surface) == vk.TRUE;
|
||||
// if (graphics and present) {
|
||||
// break @intCast(idx);
|
||||
// }
|
||||
// } else {
|
||||
// continue :pdev_search;
|
||||
// };
|
||||
//
|
||||
// std.log.debug("selecting device {s}", .{std.mem.sliceTo(&props.device_name, 0)});
|
||||
//
|
||||
// const qci: []const vk.DeviceQueueCreateInfo = &.{
|
||||
// vk.DeviceQueueCreateInfo{
|
||||
// .queue_family_index = graphics_family,
|
||||
// .queue_count = 1,
|
||||
// .p_queue_priorities = &[_]f32{1.0},
|
||||
// },
|
||||
// };
|
||||
//
|
||||
// const dev = try vki.createDevice(pdev, &.{
|
||||
// .queue_create_info_count = @intCast(qci.len),
|
||||
// .p_queue_create_infos = qci.ptr,
|
||||
// .enabled_extension_count = @intCast(required_device_extensions.len),
|
||||
// .pp_enabled_extension_names = required_device_extensions.ptr,
|
||||
// .p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{
|
||||
// .dynamic_rendering = vk.TRUE,
|
||||
// },
|
||||
// }, null);
|
||||
// const vkd = try Device.Wrapper.load(dev, vki.dispatch.vkGetDeviceProcAddr);
|
||||
// errdefer vkd.destroyDevice(dev, null);
|
||||
//
|
||||
// return .{ pdev, dev, vkd, graphics_family };
|
||||
// }
|
||||
//
|
||||
// return error.NoSuitableDevice;
|
||||
// }
|
||||
//
|
||||
// pub fn find_surface_format(
|
||||
// pdev: vk.PhysicalDevice,
|
||||
// vki: InstanceDispatch,
|
||||
// surface: vk.SurfaceKHR,
|
||||
// preferred: vk.SurfaceFormatKHR,
|
||||
// ) !vk.SurfaceFormatKHR {
|
||||
// var formats_buf: [64]vk.SurfaceFormatKHR = undefined;
|
||||
// var formats_count: u32 = @intCast(formats_buf.len);
|
||||
// _ = try vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, surface, &formats_count, &formats_buf);
|
||||
// const formats = formats_buf[0..formats_count];
|
||||
//
|
||||
// for (formats) |format| {
|
||||
// if (std.meta.eql(format, preferred)) {
|
||||
// return format;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return formats[0];
|
||||
// }
|
||||
//
|
||||
// pub fn find_present_mode(
|
||||
// pdev: vk.PhysicalDevice,
|
||||
// vki: InstanceDispatch,
|
||||
// surface: vk.SurfaceKHR,
|
||||
// preferred: vk.PresentModeKHR,
|
||||
// ) !vk.PresentModeKHR {
|
||||
// var modes_buf: [8]vk.PresentModeKHR = undefined;
|
||||
// var modes_count: u32 = @intCast(modes_buf.len);
|
||||
// _ = try vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, surface, &modes_count, &modes_buf);
|
||||
// const modes = modes_buf[0..modes_count];
|
||||
//
|
||||
// for (modes) |mode| {
|
||||
// if (std.meta.eql(mode, preferred)) {
|
||||
// return mode;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return .mailbox_khr;
|
||||
// }
|
||||
//
|
||||
// pub fn find_swap_extent(
|
||||
// pdev: vk.PhysicalDevice,
|
||||
// vki: InstanceDispatch,
|
||||
// surface: vk.SurfaceKHR,
|
||||
// window: *c.GLFWwindow,
|
||||
// ) !vk.Extent2D {
|
||||
// const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, surface);
|
||||
// var extent = caps.current_extent;
|
||||
//
|
||||
// if (extent.width == std.math.maxInt(u32)) {
|
||||
// c.glfwGetFramebufferSize(window, @ptrCast(&extent.width), @ptrCast(&extent.height));
|
||||
// extent.width = std.math.clamp(extent.width, caps.min_image_extent.width, caps.max_image_extent.width);
|
||||
// extent.height = std.math.clamp(extent.height, caps.min_image_extent.height, caps.max_image_extent.height);
|
||||
// }
|
||||
//
|
||||
// return extent;
|
||||
// }
|
||||
//
|
||||
// pub fn find_swap_image_count(
|
||||
// pdev: vk.PhysicalDevice,
|
||||
// vki: InstanceDispatch,
|
||||
// surface: vk.SurfaceKHR,
|
||||
// ) !u32 {
|
||||
// const caps = try vki.getPhysicalDeviceSurfaceCapabilitiesKHR(pdev, surface);
|
||||
// var count = @max(3, caps.min_image_count + 1);
|
||||
// if (caps.max_image_count > 0) {
|
||||
// count = @min(count, caps.max_image_count);
|
||||
// }
|
||||
// return count;
|
||||
// }
|
||||
|
||||
pub fn uploadData(
|
||||
comptime T: type,
|
||||
pdev: vk.PhysicalDevice,
|
||||
vki: InstanceDispatch,
|
||||
vki: Instance.Wrapper,
|
||||
dev: vk.Device,
|
||||
vkd: DeviceDispatch,
|
||||
vkd: Device.Wrapper,
|
||||
queue: vk.Queue,
|
||||
pool: vk.CommandPool,
|
||||
buffer: vk.Buffer,
|
||||
@@ -326,7 +326,7 @@ pub fn copyBuffer(
|
||||
dst: vk.Buffer,
|
||||
src: vk.Buffer,
|
||||
size: vk.DeviceSize,
|
||||
vkd: DeviceDispatch,
|
||||
vkd: Device.Wrapper,
|
||||
) !void {
|
||||
var cmdbuf: vk.CommandBuffer = undefined;
|
||||
try vkd.allocateCommandBuffers(dev, &.{
|
||||
@@ -371,7 +371,7 @@ pub const VkAllocator = struct {
|
||||
|
||||
pub fn init(
|
||||
pdev: vk.PhysicalDevice,
|
||||
vki: InstanceDispatch,
|
||||
vki: Instance.Wrapper,
|
||||
) VkAllocator {
|
||||
const props = vki.getPhysicalDeviceMemoryProperties(pdev);
|
||||
|
||||
@@ -384,7 +384,7 @@ pub const VkAllocator = struct {
|
||||
pub fn alloc(
|
||||
self: VkAllocator,
|
||||
dev: vk.Device,
|
||||
vkd: DeviceDispatch,
|
||||
vkd: Device.Wrapper,
|
||||
reqs: vk.MemoryRequirements,
|
||||
flags: vk.MemoryPropertyFlags,
|
||||
) !vk.DeviceMemory {
|
||||
@@ -403,82 +403,82 @@ pub const VkAllocator = struct {
|
||||
}
|
||||
};
|
||||
|
||||
pub const BaseDispatch = vk.BaseWrapper(.{
|
||||
.createInstance = true,
|
||||
.getInstanceProcAddr = true,
|
||||
});
|
||||
// pub const BaseDispatch = vk.BaseWrapper(.{
|
||||
// .createInstance = true,
|
||||
// .getInstanceProcAddr = true,
|
||||
// });
|
||||
|
||||
pub const InstanceDispatch = vk.InstanceWrapper(.{
|
||||
.destroyInstance = true,
|
||||
.createDevice = true,
|
||||
.destroySurfaceKHR = true,
|
||||
.enumeratePhysicalDevices = true,
|
||||
.getPhysicalDeviceProperties = true,
|
||||
.enumerateDeviceExtensionProperties = true,
|
||||
.getPhysicalDeviceSurfaceFormatsKHR = true,
|
||||
.getPhysicalDeviceSurfacePresentModesKHR = true,
|
||||
.getPhysicalDeviceSurfaceCapabilitiesKHR = true,
|
||||
.getPhysicalDeviceQueueFamilyProperties = true,
|
||||
.getPhysicalDeviceSurfaceSupportKHR = true,
|
||||
.getPhysicalDeviceMemoryProperties = true,
|
||||
.getDeviceProcAddr = true,
|
||||
.createDebugUtilsMessengerEXT = use_debug_messenger,
|
||||
.destroyDebugUtilsMessengerEXT = use_debug_messenger,
|
||||
});
|
||||
// pub const Instance.Wrapper = vk.InstanceWrapper(.{
|
||||
// .destroyInstance = true,
|
||||
// .createDevice = true,
|
||||
// .destroySurfaceKHR = true,
|
||||
// .enumeratePhysicalDevices = true,
|
||||
// .getPhysicalDeviceProperties = true,
|
||||
// .enumerateDeviceExtensionProperties = true,
|
||||
// .getPhysicalDeviceSurfaceFormatsKHR = true,
|
||||
// .getPhysicalDeviceSurfacePresentModesKHR = true,
|
||||
// .getPhysicalDeviceSurfaceCapabilitiesKHR = true,
|
||||
// .getPhysicalDeviceQueueFamilyProperties = true,
|
||||
// .getPhysicalDeviceSurfaceSupportKHR = true,
|
||||
// .getPhysicalDeviceMemoryProperties = true,
|
||||
// .getDeviceProcAddr = true,
|
||||
// .createDebugUtilsMessengerEXT = use_debug_messenger,
|
||||
// .destroyDebugUtilsMessengerEXT = use_debug_messenger,
|
||||
// });
|
||||
|
||||
pub const DeviceDispatch = vk.DeviceWrapper(.{
|
||||
.destroyDevice = true,
|
||||
.getDeviceQueue = true,
|
||||
.createSemaphore = true,
|
||||
.createFence = true,
|
||||
.createImageView = true,
|
||||
.destroyImageView = true,
|
||||
.destroySemaphore = true,
|
||||
.destroyFence = true,
|
||||
.getSwapchainImagesKHR = true,
|
||||
.createSwapchainKHR = true,
|
||||
.destroySwapchainKHR = true,
|
||||
.acquireNextImageKHR = true,
|
||||
.deviceWaitIdle = true,
|
||||
.waitForFences = true,
|
||||
.resetFences = true,
|
||||
.queueSubmit = true,
|
||||
.queuePresentKHR = true,
|
||||
.createCommandPool = true,
|
||||
.destroyCommandPool = true,
|
||||
.allocateCommandBuffers = true,
|
||||
.freeCommandBuffers = true,
|
||||
.queueWaitIdle = true,
|
||||
.createShaderModule = true,
|
||||
.destroyShaderModule = true,
|
||||
.createPipelineLayout = true,
|
||||
.destroyPipelineLayout = true,
|
||||
.createGraphicsPipelines = true,
|
||||
.destroyPipeline = true,
|
||||
.beginCommandBuffer = true,
|
||||
.endCommandBuffer = true,
|
||||
.allocateMemory = true,
|
||||
.freeMemory = true,
|
||||
.createBuffer = true,
|
||||
.destroyBuffer = true,
|
||||
.getBufferMemoryRequirements = true,
|
||||
.mapMemory = true,
|
||||
.unmapMemory = true,
|
||||
.bindBufferMemory = true,
|
||||
.cmdBeginRenderPass = true,
|
||||
.cmdEndRenderPass = true,
|
||||
.cmdBindPipeline = true,
|
||||
.cmdDraw = true,
|
||||
.cmdDrawIndexed = true,
|
||||
.cmdSetViewport = true,
|
||||
.cmdSetScissor = true,
|
||||
.cmdBindVertexBuffers = true,
|
||||
.cmdBindIndexBuffer = true,
|
||||
.cmdCopyBuffer = true,
|
||||
.cmdBeginRenderingKHR = true,
|
||||
.cmdEndRenderingKHR = true,
|
||||
.cmdPipelineBarrier = true,
|
||||
});
|
||||
// pub const Device.Wrapper = vk.DeviceWrapper(.{
|
||||
// .destroyDevice = true,
|
||||
// .getDeviceQueue = true,
|
||||
// .createSemaphore = true,
|
||||
// .createFence = true,
|
||||
// .createImageView = true,
|
||||
// .destroyImageView = true,
|
||||
// .destroySemaphore = true,
|
||||
// .destroyFence = true,
|
||||
// .getSwapchainImagesKHR = true,
|
||||
// .createSwapchainKHR = true,
|
||||
// .destroySwapchainKHR = true,
|
||||
// .acquireNextImageKHR = true,
|
||||
// .deviceWaitIdle = true,
|
||||
// .waitForFences = true,
|
||||
// .resetFences = true,
|
||||
// .queueSubmit = true,
|
||||
// .queuePresentKHR = true,
|
||||
// .createCommandPool = true,
|
||||
// .destroyCommandPool = true,
|
||||
// .allocateCommandBuffers = true,
|
||||
// .freeCommandBuffers = true,
|
||||
// .queueWaitIdle = true,
|
||||
// .createShaderModule = true,
|
||||
// .destroyShaderModule = true,
|
||||
// .createPipelineLayout = true,
|
||||
// .destroyPipelineLayout = true,
|
||||
// .createGraphicsPipelines = true,
|
||||
// .destroyPipeline = true,
|
||||
// .beginCommandBuffer = true,
|
||||
// .endCommandBuffer = true,
|
||||
// .allocateMemory = true,
|
||||
// .freeMemory = true,
|
||||
// .createBuffer = true,
|
||||
// .destroyBuffer = true,
|
||||
// .getBufferMemoryRequirements = true,
|
||||
// .mapMemory = true,
|
||||
// .unmapMemory = true,
|
||||
// .bindBufferMemory = true,
|
||||
// .cmdBeginRenderPass = true,
|
||||
// .cmdEndRenderPass = true,
|
||||
// .cmdBindPipeline = true,
|
||||
// .cmdDraw = true,
|
||||
// .cmdDrawIndexed = true,
|
||||
// .cmdSetViewport = true,
|
||||
// .cmdSetScissor = true,
|
||||
// .cmdBindVertexBuffers = true,
|
||||
// .cmdBindIndexBuffer = true,
|
||||
// .cmdCopyBuffer = true,
|
||||
// .cmdBeginRenderingKHR = true,
|
||||
// .cmdEndRenderingKHR = true,
|
||||
// .cmdPipelineBarrier = true,
|
||||
// });
|
||||
|
||||
pub fn debug_callback(
|
||||
msg_severity: vk.DebugUtilsMessageSeverityFlagsEXT,
|
||||
|
32
src/gfx/Base.zig
Normal file
32
src/gfx/Base.zig
Normal file
@@ -0,0 +1,32 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const vk = @import("vk");
|
||||
const c = @import("../c.zig");
|
||||
|
||||
const Self = @This();
|
||||
|
||||
vkb: Wrapper,
|
||||
|
||||
pub fn init() !Self {
|
||||
if (c.glfwInit() != c.GLFW_TRUE)
|
||||
return error.GLFWInitFailed;
|
||||
errdefer c.glfwTerminate();
|
||||
|
||||
if (c.glfwVulkanSupported() != c.GLFW_TRUE) {
|
||||
return error.GLFWNoVulkan;
|
||||
}
|
||||
|
||||
return .{
|
||||
.vkb = try Wrapper.load(c.glfwGetInstanceProcAddress),
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(_: Self) void {
|
||||
c.glfwTerminate();
|
||||
}
|
||||
|
||||
pub const Wrapper = vk.BaseWrapper(.{
|
||||
.createInstance = true,
|
||||
.getInstanceProcAddr = true,
|
||||
});
|
5
src/gfx/Context.zig
Normal file
5
src/gfx/Context.zig
Normal file
@@ -0,0 +1,5 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
const vk = @import("vk");
|
||||
const c = @import("../c.zig");
|
||||
const gfx = @import("../c.zig");
|
231
src/gfx/Device.zig
Normal file
231
src/gfx/Device.zig
Normal file
@@ -0,0 +1,231 @@
|
||||
//! The point here is to select _a_ physical device and create a logical device around it.
|
||||
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const vk = @import("vk");
|
||||
const c = @import("../c.zig");
|
||||
|
||||
const Instance = @import("Instance.zig");
|
||||
const Window = @import("Window.zig");
|
||||
|
||||
const Self = @This();
|
||||
|
||||
const required_extensions: []const [*:0]const u8 = &.{
|
||||
vk.extension_info.khr_swapchain.name,
|
||||
vk.extension_info.khr_dynamic_rendering.name,
|
||||
};
|
||||
|
||||
const preferred_surface_format: vk.SurfaceFormatKHR = .{
|
||||
.color_space = .srgb_nonlinear_khr,
|
||||
.format = .r8g8b8a8_sint,
|
||||
};
|
||||
|
||||
const preferred_present_mode: vk.PresentModeKHR = .mailbox_khr;
|
||||
|
||||
dev: vk.Device,
|
||||
pdev: vk.PhysicalDevice,
|
||||
vkd: Wrapper,
|
||||
|
||||
inst: *const Instance,
|
||||
win: *const Window,
|
||||
|
||||
format: vk.SurfaceFormatKHR,
|
||||
mode: vk.PresentModeKHR,
|
||||
family: u32,
|
||||
queue: vk.Queue,
|
||||
|
||||
pool: vk.CommandPool,
|
||||
|
||||
pub fn init(
|
||||
ally: std.mem.Allocator,
|
||||
inst: *const Instance,
|
||||
win: *const Window,
|
||||
) !Self {
|
||||
var pdev_count: u32 = undefined;
|
||||
_ = try inst.vki.enumeratePhysicalDevices(inst.ref, &pdev_count, null);
|
||||
const pdevs = try ally.alloc(vk.PhysicalDevice, pdev_count);
|
||||
defer ally.free(pdevs);
|
||||
_ = try inst.vki.enumeratePhysicalDevices(inst.ref, &pdev_count, pdevs.ptr);
|
||||
|
||||
for (pdevs) |pdev| {
|
||||
return wrap(ally, inst, win, pdev) catch continue;
|
||||
}
|
||||
|
||||
return error.NoSuitableDevice;
|
||||
}
|
||||
|
||||
pub fn wrap(
|
||||
ally: std.mem.Allocator,
|
||||
inst: *const Instance,
|
||||
win: *const Window,
|
||||
pdev: vk.PhysicalDevice,
|
||||
) !Self {
|
||||
const props = inst.vki.getPhysicalDeviceProperties(pdev);
|
||||
if (props.device_type != .discrete_gpu) return error.NotDiscrete;
|
||||
|
||||
var format_count: u32 = undefined;
|
||||
_ = try inst.vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, win.surface, &format_count, null);
|
||||
if (format_count == 0) return error.NoSurfaceFormat;
|
||||
const formats = try ally.alloc(vk.SurfaceFormatKHR, format_count);
|
||||
defer ally.free(formats);
|
||||
_ = try inst.vki.getPhysicalDeviceSurfaceFormatsKHR(pdev, win.surface, &format_count, formats.ptr);
|
||||
|
||||
const format = for (formats) |f| {
|
||||
if (std.meta.eql(f, preferred_surface_format))
|
||||
break f;
|
||||
} else formats[0];
|
||||
|
||||
var mode_count: u32 = undefined;
|
||||
_ = try inst.vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, win.surface, &mode_count, null);
|
||||
if (mode_count == 0) return error.NoSurfaceMode;
|
||||
const modes = try ally.alloc(vk.PresentModeKHR, mode_count);
|
||||
defer ally.free(modes);
|
||||
_ = try inst.vki.getPhysicalDeviceSurfacePresentModesKHR(pdev, win.surface, &mode_count, modes.ptr);
|
||||
|
||||
const mode = for (modes) |m| {
|
||||
if (std.meta.eql(m, preferred_present_mode))
|
||||
break m;
|
||||
} else modes[0];
|
||||
|
||||
var ext_count: u32 = undefined;
|
||||
_ = try inst.vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, null);
|
||||
const exts = try ally.alloc(vk.ExtensionProperties, ext_count);
|
||||
defer ally.free(exts);
|
||||
_ = try inst.vki.enumerateDeviceExtensionProperties(pdev, null, &ext_count, exts.ptr);
|
||||
|
||||
for (required_extensions) |name| {
|
||||
for (exts) |ext| {
|
||||
if (std.mem.eql(
|
||||
u8,
|
||||
std.mem.span(name),
|
||||
std.mem.sliceTo(&ext.extension_name, 0),
|
||||
)) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
return error.MissingRequiredExtension;
|
||||
}
|
||||
}
|
||||
|
||||
var family_count: u32 = undefined;
|
||||
inst.vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null);
|
||||
const families = try ally.alloc(vk.QueueFamilyProperties, family_count);
|
||||
defer ally.free(families);
|
||||
inst.vki.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr);
|
||||
|
||||
// just find one family that does graphics and present, so we can use exclusive sharing
|
||||
// on the swapchain. apparently most hardware supports this. logic for queue allocation
|
||||
// and swapchain creation is so much simpler this way. swapchain creation needs to know
|
||||
// the list of queue family indices which will have access to the images, and there's a
|
||||
// performance penalty to allow concurrent access to multiple queue families.
|
||||
//
|
||||
// multiple _queues_ may have exclusive access, but only if they're in the smae family.
|
||||
|
||||
const family: u32 = for (families, 0..) |family, idx| {
|
||||
const graphics = family.queue_flags.graphics_bit;
|
||||
const present = try inst.vki.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(idx), win.surface) == vk.TRUE;
|
||||
if (graphics and present) break @intCast(idx);
|
||||
} else {
|
||||
return error.NoSuitableQueueFamily;
|
||||
};
|
||||
|
||||
const qci: []const vk.DeviceQueueCreateInfo = &.{
|
||||
vk.DeviceQueueCreateInfo{
|
||||
.queue_family_index = family,
|
||||
.queue_count = 1,
|
||||
.p_queue_priorities = &[_]f32{1.0},
|
||||
},
|
||||
};
|
||||
|
||||
const dev = try inst.vki.createDevice(pdev, &.{
|
||||
.queue_create_info_count = @intCast(qci.len),
|
||||
.p_queue_create_infos = qci.ptr,
|
||||
.enabled_extension_count = @intCast(required_extensions.len),
|
||||
.pp_enabled_extension_names = required_extensions.ptr,
|
||||
.p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{
|
||||
.dynamic_rendering = vk.TRUE,
|
||||
},
|
||||
}, null);
|
||||
const vkd = try Wrapper.load(dev, inst.vki.dispatch.vkGetDeviceProcAddr);
|
||||
errdefer vkd.destroyDevice(dev, null);
|
||||
|
||||
const pool = try vkd.createCommandPool(dev, &.{
|
||||
.queue_family_index = family,
|
||||
}, null);
|
||||
errdefer vkd.destroyCommandPool(dev, pool, null);
|
||||
|
||||
const queue = vkd.getDeviceQueue(dev, family, 0);
|
||||
|
||||
return .{
|
||||
.dev = dev,
|
||||
.pdev = pdev,
|
||||
.vkd = vkd,
|
||||
.inst = inst,
|
||||
.win = win,
|
||||
.format = format,
|
||||
.mode = mode,
|
||||
.pool = pool,
|
||||
.family = family,
|
||||
.queue = queue,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.vkd.destroyCommandPool(self.dev, self.pool, null);
|
||||
self.vkd.destroyDevice(self.dev, null);
|
||||
}
|
||||
|
||||
pub const Wrapper = vk.DeviceWrapper(.{
|
||||
.destroyDevice = true,
|
||||
.getDeviceQueue = true,
|
||||
.createSemaphore = true,
|
||||
.createFence = true,
|
||||
.createImageView = true,
|
||||
.destroyImageView = true,
|
||||
.destroySemaphore = true,
|
||||
.destroyFence = true,
|
||||
.getSwapchainImagesKHR = true,
|
||||
.createSwapchainKHR = true,
|
||||
.destroySwapchainKHR = true,
|
||||
.acquireNextImageKHR = true,
|
||||
.deviceWaitIdle = true,
|
||||
.waitForFences = true,
|
||||
.resetFences = true,
|
||||
.queueSubmit = true,
|
||||
.queuePresentKHR = true,
|
||||
.createCommandPool = true,
|
||||
.destroyCommandPool = true,
|
||||
.allocateCommandBuffers = true,
|
||||
.freeCommandBuffers = true,
|
||||
.queueWaitIdle = true,
|
||||
.createShaderModule = true,
|
||||
.destroyShaderModule = true,
|
||||
.createPipelineLayout = true,
|
||||
.destroyPipelineLayout = true,
|
||||
.createGraphicsPipelines = true,
|
||||
.destroyPipeline = true,
|
||||
.beginCommandBuffer = true,
|
||||
.endCommandBuffer = true,
|
||||
.allocateMemory = true,
|
||||
.freeMemory = true,
|
||||
.createBuffer = true,
|
||||
.destroyBuffer = true,
|
||||
.getBufferMemoryRequirements = true,
|
||||
.mapMemory = true,
|
||||
.unmapMemory = true,
|
||||
.bindBufferMemory = true,
|
||||
.cmdBeginRenderPass = true,
|
||||
.cmdEndRenderPass = true,
|
||||
.cmdBindPipeline = true,
|
||||
.cmdDraw = true,
|
||||
.cmdDrawIndexed = true,
|
||||
.cmdSetViewport = true,
|
||||
.cmdSetScissor = true,
|
||||
.cmdBindVertexBuffers = true,
|
||||
.cmdBindIndexBuffer = true,
|
||||
.cmdCopyBuffer = true,
|
||||
.cmdBeginRenderingKHR = true,
|
||||
.cmdEndRenderingKHR = true,
|
||||
.cmdPipelineBarrier = true,
|
||||
});
|
165
src/gfx/Instance.zig
Normal file
165
src/gfx/Instance.zig
Normal file
@@ -0,0 +1,165 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const vk = @import("vk");
|
||||
const c = @import("../c.zig");
|
||||
|
||||
const Base = @import("Base.zig");
|
||||
|
||||
const Self = @This();
|
||||
|
||||
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 use_debug_messenger = switch (builtin.mode) {
|
||||
.Debug, .ReleaseSafe => true,
|
||||
.ReleaseSmall, .ReleaseFast => false,
|
||||
};
|
||||
|
||||
ref: vk.Instance,
|
||||
vki: Wrapper,
|
||||
base: *const Base,
|
||||
|
||||
messenger: if (use_debug_messenger) vk.DebugUtilsMessengerEXT else void,
|
||||
|
||||
pub fn init(
|
||||
base: *const Base,
|
||||
) !Self {
|
||||
var exts: std.BoundedArray([*:0]const u8, 32) = .{};
|
||||
var layers: std.BoundedArray([*:0]const u8, 32) = .{};
|
||||
|
||||
if (use_debug_messenger) {
|
||||
try exts.appendSlice(&.{
|
||||
vk.extension_info.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 exts.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,
|
||||
};
|
||||
|
||||
const ref = try base.vkb.createInstance(&.{
|
||||
.p_application_info = &app_info,
|
||||
.enabled_extension_count = @intCast(exts.len),
|
||||
.pp_enabled_extension_names = &exts.buffer,
|
||||
.enabled_layer_count = @intCast(layers.len),
|
||||
.pp_enabled_layer_names = &layers.buffer,
|
||||
.p_next = if (use_debug_messenger) &mci else null,
|
||||
}, null);
|
||||
|
||||
const vki = try Wrapper.load(ref, base.vkb.dispatch.vkGetInstanceProcAddr);
|
||||
errdefer vki.destroyInstance(ref, null);
|
||||
|
||||
const messenger = if (use_debug_messenger)
|
||||
try vki.createDebugUtilsMessengerEXT(ref, &mci, null)
|
||||
else
|
||||
void{};
|
||||
|
||||
errdefer if (use_debug_messenger)
|
||||
vki.destroyDebugUtilsMessengerEXT(ref, messenger, null);
|
||||
|
||||
return .{
|
||||
.ref = ref,
|
||||
.vki = vki,
|
||||
.base = base,
|
||||
.messenger = messenger,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
if (use_debug_messenger)
|
||||
self.vki.destroyDebugUtilsMessengerEXT(self.ref, self.messenger, null);
|
||||
self.vki.destroyInstance(self.ref, null);
|
||||
}
|
||||
|
||||
pub const Wrapper = vk.InstanceWrapper(.{
|
||||
.destroyInstance = true,
|
||||
.createDevice = true,
|
||||
.destroySurfaceKHR = true,
|
||||
.enumeratePhysicalDevices = true,
|
||||
.getPhysicalDeviceProperties = true,
|
||||
.enumerateDeviceExtensionProperties = true,
|
||||
.getPhysicalDeviceSurfaceFormatsKHR = true,
|
||||
.getPhysicalDeviceSurfacePresentModesKHR = true,
|
||||
.getPhysicalDeviceSurfaceCapabilitiesKHR = true,
|
||||
.getPhysicalDeviceQueueFamilyProperties = true,
|
||||
.getPhysicalDeviceSurfaceSupportKHR = true,
|
||||
.getPhysicalDeviceMemoryProperties = true,
|
||||
.getDeviceProcAddr = true,
|
||||
.createDebugUtilsMessengerEXT = use_debug_messenger,
|
||||
.destroyDebugUtilsMessengerEXT = use_debug_messenger,
|
||||
});
|
||||
|
||||
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.getStderrMutex().lock();
|
||||
defer std.debug.getStderrMutex().unlock();
|
||||
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;
|
||||
}
|
169
src/gfx/Swapchain.zig
Normal file
169
src/gfx/Swapchain.zig
Normal file
@@ -0,0 +1,169 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const vk = @import("vk");
|
||||
const c = @import("../c.zig");
|
||||
|
||||
const Instance = @import("Instance.zig");
|
||||
const Window = @import("Window.zig");
|
||||
const Device = @import("Device.zig");
|
||||
|
||||
const Self = @This();
|
||||
|
||||
pub const ChainImage = struct {
|
||||
image: vk.Image = .null_handle,
|
||||
view: vk.ImageView = .null_handle,
|
||||
cmdbuf: vk.CommandBuffer = .null_handle,
|
||||
fence: vk.Fence = .null_handle,
|
||||
image_available: vk.Semaphore = .null_handle,
|
||||
render_finished: vk.Semaphore = .null_handle,
|
||||
};
|
||||
|
||||
ally: std.mem.Allocator,
|
||||
|
||||
ref: vk.SwapchainKHR,
|
||||
dev: *const Device,
|
||||
|
||||
extent: vk.Extent2D,
|
||||
min_image_count: u32,
|
||||
chain: std.MultiArrayList(ChainImage),
|
||||
|
||||
pub fn create(ally: std.mem.Allocator, dev: *const Device) !Self {
|
||||
const caps = try dev.inst.vki.getPhysicalDeviceSurfaceCapabilitiesKHR(dev.pdev, dev.win.surface);
|
||||
var min_image_count = @max(3, caps.min_image_count + 1);
|
||||
if (caps.max_image_count > 0) {
|
||||
min_image_count = @min(min_image_count, caps.max_image_count);
|
||||
}
|
||||
|
||||
return .{
|
||||
.ally = ally,
|
||||
.ref = .null_handle,
|
||||
.dev = dev,
|
||||
.extent = undefined,
|
||||
.min_image_count = min_image_count,
|
||||
.chain = .{},
|
||||
};
|
||||
}
|
||||
|
||||
pub fn init(self: *Self) !void {
|
||||
const caps = try self.dev.inst.vki.getPhysicalDeviceSurfaceCapabilitiesKHR(self.dev.pdev, self.dev.win.surface);
|
||||
|
||||
self.extent = caps.current_extent;
|
||||
if (caps.current_extent.width == std.math.maxInt(u32)) {
|
||||
c.glfwGetFramebufferSize(self.dev.win.ref, @ptrCast(&self.extent.width), @ptrCast(&self.extent.height));
|
||||
}
|
||||
self.extent.width = std.math.clamp(self.extent.width, caps.min_image_extent.width, caps.max_image_extent.width);
|
||||
self.extent.height = std.math.clamp(self.extent.height, caps.min_image_extent.height, caps.max_image_extent.height);
|
||||
|
||||
const prev = self.ref;
|
||||
self.ref = try self.dev.vkd.createSwapchainKHR(self.dev.dev, &.{
|
||||
.surface = self.dev.win.surface,
|
||||
.min_image_count = self.min_image_count,
|
||||
.image_format = self.dev.format.format,
|
||||
.image_color_space = self.dev.format.color_space,
|
||||
.image_extent = self.extent,
|
||||
.image_array_layers = 1,
|
||||
.image_usage = .{ .color_attachment_bit = true },
|
||||
.image_sharing_mode = .exclusive,
|
||||
.pre_transform = .{ .identity_bit_khr = true },
|
||||
.composite_alpha = .{ .opaque_bit_khr = true },
|
||||
.present_mode = self.dev.mode,
|
||||
.clipped = vk.TRUE,
|
||||
.old_swapchain = prev,
|
||||
}, null);
|
||||
self.dev.vkd.destroySwapchainKHR(self.dev.dev, prev, null);
|
||||
|
||||
var image_count: u32 = undefined;
|
||||
_ = try self.dev.vkd.getSwapchainImagesKHR(self.dev.dev, self.ref, &image_count, null);
|
||||
|
||||
// todo try to reuse contents if possible.
|
||||
// not even sure at this point which parts can be safely reused. the trick to fix the tearing while resizing
|
||||
// on laptop is probably in doing this correctly, to present any remaining images that can be presented.
|
||||
|
||||
self.deinit_chain();
|
||||
|
||||
try self.chain.resize(self.ally, image_count);
|
||||
_ = try self.dev.vkd.getSwapchainImagesKHR(
|
||||
self.dev.dev,
|
||||
self.ref,
|
||||
&image_count,
|
||||
self.chain.items(.image).ptr,
|
||||
);
|
||||
|
||||
try self.init_chain();
|
||||
}
|
||||
|
||||
// requires self.chain.len and self.chain.items(.image) be populated by getSwapchainImagesKHR
|
||||
fn init_chain(self: *Self) !void {
|
||||
@memset(self.chain.items(.view), .null_handle);
|
||||
@memset(self.chain.items(.cmdbuf), .null_handle);
|
||||
@memset(self.chain.items(.fence), .null_handle);
|
||||
@memset(self.chain.items(.image_available), .null_handle);
|
||||
@memset(self.chain.items(.render_finished), .null_handle);
|
||||
errdefer self.deinit_chain();
|
||||
|
||||
for (self.chain.items(.image), self.chain.items(.view)) |image, *view| {
|
||||
view.* = try self.dev.vkd.createImageView(self.dev.dev, &.{
|
||||
.image = image,
|
||||
.view_type = .@"2d",
|
||||
.format = self.dev.format.format,
|
||||
.components = .{ .r = .identity, .g = .identity, .b = .identity, .a = .identity },
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 1,
|
||||
},
|
||||
}, null);
|
||||
}
|
||||
|
||||
for (self.chain.items(.fence)) |*fence| {
|
||||
fence.* = try self.dev.vkd.createFence(self.dev.dev, &.{
|
||||
.flags = .{ .signaled_bit = true },
|
||||
}, null);
|
||||
}
|
||||
|
||||
for (self.chain.items(.image_available)) |*sem| {
|
||||
sem.* = try self.dev.vkd.createSemaphore(self.dev.dev, &.{}, null);
|
||||
}
|
||||
|
||||
for (self.chain.items(.render_finished)) |*sem| {
|
||||
sem.* = try self.dev.vkd.createSemaphore(self.dev.dev, &.{}, null);
|
||||
}
|
||||
|
||||
try self.dev.vkd.allocateCommandBuffers(self.dev.dev, &.{
|
||||
.command_pool = self.dev.pool,
|
||||
.command_buffer_count = @intCast(self.chain.len),
|
||||
.level = .primary,
|
||||
}, self.chain.items(.cmdbuf).ptr);
|
||||
}
|
||||
|
||||
fn deinit_chain(self: Self) void {
|
||||
for (self.chain.items(.view)) |view| {
|
||||
self.dev.vkd.destroyImageView(self.dev.dev, view, null);
|
||||
}
|
||||
for (self.chain.items(.fence)) |fence| {
|
||||
self.dev.vkd.destroyFence(self.dev.dev, fence, null);
|
||||
}
|
||||
for (self.chain.items(.image_available)) |sem| {
|
||||
self.dev.vkd.destroySemaphore(self.dev.dev, sem, null);
|
||||
}
|
||||
for (self.chain.items(.render_finished)) |sem| {
|
||||
self.dev.vkd.destroySemaphore(self.dev.dev, sem, null);
|
||||
}
|
||||
if (self.chain.len > 0) {
|
||||
self.dev.vkd.freeCommandBuffers(
|
||||
self.dev.dev,
|
||||
self.dev.pool,
|
||||
@intCast(self.chain.len),
|
||||
self.chain.items(.cmdbuf).ptr,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn deinit(self: *Self) void {
|
||||
self.deinit_chain();
|
||||
self.chain.deinit(self.ally);
|
||||
self.dev.vkd.destroySwapchainKHR(self.dev.dev, self.ref, null);
|
||||
}
|
41
src/gfx/Window.zig
Normal file
41
src/gfx/Window.zig
Normal file
@@ -0,0 +1,41 @@
|
||||
const std = @import("std");
|
||||
const builtin = @import("builtin");
|
||||
|
||||
const vk = @import("vk");
|
||||
const c = @import("../c.zig");
|
||||
|
||||
const Base = @import("Base.zig");
|
||||
const Instance = @import("Instance.zig");
|
||||
|
||||
const Self = @This();
|
||||
|
||||
ref: *c.GLFWwindow,
|
||||
surface: vk.SurfaceKHR,
|
||||
|
||||
inst: *const Instance,
|
||||
|
||||
pub fn init(inst: *const Instance, title: [*:0]const u8, extent: vk.Extent2D) !Self {
|
||||
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);
|
||||
|
||||
const ref = c.glfwCreateWindow(@intCast(extent.width), @intCast(extent.height), title, null, null) orelse
|
||||
return error.GLFWWindowCreateFailed;
|
||||
errdefer c.glfwDestroyWindow(ref);
|
||||
|
||||
var surface: vk.SurfaceKHR = undefined;
|
||||
if (c.glfwCreateWindowSurface(inst.ref, ref, null, &surface) != .success)
|
||||
return error.GLFWWindowSurfaceFailed;
|
||||
errdefer inst.vki.destroySurfaceKHR(inst.ref, surface, null);
|
||||
|
||||
return .{
|
||||
.ref = ref,
|
||||
.surface = surface,
|
||||
.inst = inst,
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Self) void {
|
||||
self.inst.vki.destroySurfaceKHR(self.inst.ref, self.surface, null);
|
||||
c.glfwDestroyWindow(self.ref);
|
||||
}
|
370
src/main.zig
370
src/main.zig
@@ -50,113 +50,104 @@ const vertices = [_]Vertex{
|
||||
|
||||
const indices = [_]Index{ 4, 5, 6, 6, 5, 7 };
|
||||
|
||||
const ChainImage = struct {
|
||||
image: vk.Image = .null_handle,
|
||||
view: vk.ImageView = .null_handle,
|
||||
cmdbuf: vk.CommandBuffer = .null_handle,
|
||||
fence: vk.Fence = .null_handle,
|
||||
image_available: vk.Semaphore = .null_handle,
|
||||
render_finished: vk.Semaphore = .null_handle,
|
||||
};
|
||||
// pub fn create_swapchain(
|
||||
// chain: *std.MultiArrayList(ChainImage),
|
||||
// swapchain: *vk.SwapchainKHR,
|
||||
// ally: std.mem.Allocator,
|
||||
// pdev: vk.PhysicalDevice,
|
||||
// vki: gfx.InstanceDispatch,
|
||||
// window: *c.GLFWwindow,
|
||||
// dev: vk.Device,
|
||||
// vkd: Device.Wrapper,
|
||||
// pool: vk.CommandPool,
|
||||
// surface: vk.SurfaceKHR,
|
||||
// swap_image_count: u32,
|
||||
// format: vk.SurfaceFormatKHR,
|
||||
// present_mode: vk.PresentModeKHR,
|
||||
// ) !vk.Extent2D {
|
||||
// const extent = try gfx.find_swap_extent(pdev, vki, surface, window);
|
||||
//
|
||||
// const prev_swapchain = swapchain.*;
|
||||
// swapchain.* = try vkd.createSwapchainKHR(dev, &.{
|
||||
// .surface = surface,
|
||||
// .min_image_count = swap_image_count,
|
||||
// .image_format = format.format,
|
||||
// .image_color_space = format.color_space,
|
||||
// .image_extent = extent,
|
||||
// .image_array_layers = 1,
|
||||
// .image_usage = .{ .color_attachment_bit = true },
|
||||
// .image_sharing_mode = .exclusive,
|
||||
// .pre_transform = .{ .identity_bit_khr = true },
|
||||
// .composite_alpha = .{ .opaque_bit_khr = true },
|
||||
// .present_mode = present_mode,
|
||||
// .clipped = vk.TRUE,
|
||||
// .old_swapchain = prev_swapchain,
|
||||
// }, null);
|
||||
// vkd.destroySwapchainKHR(dev, prev_swapchain, null);
|
||||
//
|
||||
// var image_count: u32 = undefined;
|
||||
// _ = try vkd.getSwapchainImagesKHR(dev, swapchain.*, &image_count, null);
|
||||
// try chain.resize(ally, image_count);
|
||||
// _ = try vkd.getSwapchainImagesKHR(dev, swapchain.*, &image_count, chain.items(.image).ptr);
|
||||
//
|
||||
// // memset so that deinit_chain will succeed with .null_handle if error part-way through a loop.
|
||||
// @memset(chain.items(.view), .null_handle);
|
||||
// @memset(chain.items(.cmdbuf), .null_handle);
|
||||
// @memset(chain.items(.fence), .null_handle);
|
||||
// @memset(chain.items(.image_available), .null_handle);
|
||||
// errdefer deinit_chain(chain.*, dev, vkd, pool);
|
||||
//
|
||||
// for (chain.items(.image), chain.items(.view)) |image, *view| {
|
||||
// view.* = try vkd.createImageView(dev, &.{
|
||||
// .image = image,
|
||||
// .view_type = .@"2d",
|
||||
// .format = format.format,
|
||||
// .components = .{ .r = .identity, .g = .identity, .b = .identity, .a = .identity },
|
||||
// .subresource_range = .{
|
||||
// .aspect_mask = .{ .color_bit = true },
|
||||
// .base_mip_level = 0,
|
||||
// .level_count = 1,
|
||||
// .base_array_layer = 0,
|
||||
// .layer_count = 1,
|
||||
// },
|
||||
// }, null);
|
||||
// }
|
||||
//
|
||||
// for (chain.items(.fence)) |*fence| {
|
||||
// fence.* = try vkd.createFence(dev, &.{ .flags = .{ .signaled_bit = true } }, null);
|
||||
// }
|
||||
//
|
||||
// for (chain.items(.image_available)) |*sem| {
|
||||
// sem.* = try vkd.createSemaphore(dev, &.{}, null);
|
||||
// }
|
||||
//
|
||||
// for (chain.items(.render_finished)) |*sem| {
|
||||
// sem.* = try vkd.createSemaphore(dev, &.{}, null);
|
||||
// }
|
||||
//
|
||||
// try vkd.allocateCommandBuffers(dev, &.{
|
||||
// .command_buffer_count = @intCast(chain.len),
|
||||
// .command_pool = pool,
|
||||
// .level = .primary,
|
||||
// }, chain.items(.cmdbuf).ptr);
|
||||
//
|
||||
// return extent;
|
||||
// }
|
||||
//
|
||||
// pub fn deinit_chain(
|
||||
// chain: std.MultiArrayList(ChainImage),
|
||||
// dev: vk.Device,
|
||||
// vkd: Device.Wrapper,
|
||||
// pool: vk.CommandPool,
|
||||
// ) void {
|
||||
// vkd.freeCommandBuffers(dev, pool, @intCast(chain.len), chain.items(.cmdbuf).ptr);
|
||||
// for (chain.items(.view)) |view| vkd.destroyImageView(dev, view, null);
|
||||
// for (chain.items(.fence)) |fence| vkd.destroyFence(dev, fence, null);
|
||||
// for (chain.items(.image_available)) |sem| vkd.destroySemaphore(dev, sem, null);
|
||||
// for (chain.items(.render_finished)) |sem| vkd.destroySemaphore(dev, sem, null);
|
||||
// }
|
||||
|
||||
pub fn create_swapchain(
|
||||
chain: *std.MultiArrayList(ChainImage),
|
||||
swapchain: *vk.SwapchainKHR,
|
||||
ally: std.mem.Allocator,
|
||||
pdev: vk.PhysicalDevice,
|
||||
vki: gfx.InstanceDispatch,
|
||||
window: *c.GLFWwindow,
|
||||
dev: vk.Device,
|
||||
vkd: gfx.DeviceDispatch,
|
||||
pool: vk.CommandPool,
|
||||
surface: vk.SurfaceKHR,
|
||||
swap_image_count: u32,
|
||||
format: vk.SurfaceFormatKHR,
|
||||
present_mode: vk.PresentModeKHR,
|
||||
) !vk.Extent2D {
|
||||
const extent = try gfx.find_swap_extent(pdev, vki, surface, window);
|
||||
|
||||
const prev_swapchain = swapchain.*;
|
||||
swapchain.* = try vkd.createSwapchainKHR(dev, &.{
|
||||
.surface = surface,
|
||||
.min_image_count = swap_image_count,
|
||||
.image_format = format.format,
|
||||
.image_color_space = format.color_space,
|
||||
.image_extent = extent,
|
||||
.image_array_layers = 1,
|
||||
.image_usage = .{ .color_attachment_bit = true },
|
||||
.image_sharing_mode = .exclusive,
|
||||
.pre_transform = .{ .identity_bit_khr = true },
|
||||
.composite_alpha = .{ .opaque_bit_khr = true },
|
||||
.present_mode = present_mode,
|
||||
.clipped = vk.TRUE,
|
||||
.old_swapchain = prev_swapchain,
|
||||
}, null);
|
||||
vkd.destroySwapchainKHR(dev, prev_swapchain, null);
|
||||
|
||||
var image_count: u32 = undefined;
|
||||
_ = try vkd.getSwapchainImagesKHR(dev, swapchain.*, &image_count, null);
|
||||
try chain.resize(ally, image_count);
|
||||
_ = try vkd.getSwapchainImagesKHR(dev, swapchain.*, &image_count, chain.items(.image).ptr);
|
||||
|
||||
// memset so that deinit_chain will succeed with .null_handle if error part-way through a loop.
|
||||
@memset(chain.items(.view), .null_handle);
|
||||
@memset(chain.items(.cmdbuf), .null_handle);
|
||||
@memset(chain.items(.fence), .null_handle);
|
||||
@memset(chain.items(.image_available), .null_handle);
|
||||
errdefer deinit_chain(chain.*, dev, vkd, pool);
|
||||
|
||||
for (chain.items(.image), chain.items(.view)) |image, *view| {
|
||||
view.* = try vkd.createImageView(dev, &.{
|
||||
.image = image,
|
||||
.view_type = .@"2d",
|
||||
.format = format.format,
|
||||
.components = .{ .r = .identity, .g = .identity, .b = .identity, .a = .identity },
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 1,
|
||||
},
|
||||
}, null);
|
||||
}
|
||||
|
||||
for (chain.items(.fence)) |*fence| {
|
||||
fence.* = try vkd.createFence(dev, &.{ .flags = .{ .signaled_bit = true } }, null);
|
||||
}
|
||||
|
||||
for (chain.items(.image_available)) |*sem| {
|
||||
sem.* = try vkd.createSemaphore(dev, &.{}, null);
|
||||
}
|
||||
|
||||
for (chain.items(.render_finished)) |*sem| {
|
||||
sem.* = try vkd.createSemaphore(dev, &.{}, null);
|
||||
}
|
||||
|
||||
try vkd.allocateCommandBuffers(dev, &.{
|
||||
.command_buffer_count = @intCast(chain.len),
|
||||
.command_pool = pool,
|
||||
.level = .primary,
|
||||
}, chain.items(.cmdbuf).ptr);
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
pub fn deinit_chain(
|
||||
chain: std.MultiArrayList(ChainImage),
|
||||
dev: vk.Device,
|
||||
vkd: gfx.DeviceDispatch,
|
||||
pool: vk.CommandPool,
|
||||
) void {
|
||||
vkd.freeCommandBuffers(dev, pool, @intCast(chain.len), chain.items(.cmdbuf).ptr);
|
||||
for (chain.items(.view)) |view| vkd.destroyImageView(dev, view, null);
|
||||
for (chain.items(.fence)) |fence| vkd.destroyFence(dev, fence, null);
|
||||
for (chain.items(.image_available)) |sem| vkd.destroySemaphore(dev, sem, null);
|
||||
for (chain.items(.render_finished)) |sem| vkd.destroySemaphore(dev, sem, null);
|
||||
}
|
||||
|
||||
fn render(dev: vk.Device, vkd: gfx.DeviceDispatch, swapchain: vk.SwapchainKHR, frame: ChainImage, queue: vk.Queue) !void {
|
||||
fn render(dev: vk.Device, vkd: Device.Wrapper, swapchain: vk.SwapchainKHR, frame: Swapchain.ChainImage, queue: vk.Queue) !void {
|
||||
_ = try vkd.waitForFences(dev, 1, @ptrCast(&frame.fence), vk.TRUE, std.math.maxInt(u64));
|
||||
|
||||
const result = try vkd.acquireNextImageKHR(
|
||||
@@ -189,130 +180,86 @@ fn render(dev: vk.Device, vkd: gfx.DeviceDispatch, swapchain: vk.SwapchainKHR, f
|
||||
});
|
||||
}
|
||||
|
||||
const Base = @import("gfx/Base.zig");
|
||||
const Instance = @import("gfx/Instance.zig");
|
||||
const Context = @import("gfx/Context.zig");
|
||||
const Window = @import("gfx/Window.zig");
|
||||
const Device = @import("gfx/Device.zig");
|
||||
const Swapchain = @import("gfx/Swapchain.zig");
|
||||
|
||||
pub fn main() !void {
|
||||
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
|
||||
defer _ = gpa.deinit();
|
||||
const ally = gpa.allocator();
|
||||
|
||||
if (c.glfwInit() != c.GLFW_TRUE) return error.GlfwInitFailed;
|
||||
defer c.glfwTerminate();
|
||||
const base = try Base.init();
|
||||
defer base.deinit();
|
||||
|
||||
if (c.glfwVulkanSupported() != c.GLFW_TRUE) {
|
||||
std.log.err("GLFW could not find libvulkan", .{});
|
||||
return error.NoVulkan;
|
||||
}
|
||||
const inst = try Instance.init(&base);
|
||||
defer inst.deinit();
|
||||
|
||||
var extent = vk.Extent2D{ .width = 800, .height = 600 };
|
||||
const win = try Window.init(&inst, "zig-glfw-vulkan", .{ .width = 800, .height = 600 });
|
||||
defer win.deinit();
|
||||
|
||||
const window = try gfx.create_window(extent, app_name);
|
||||
defer c.glfwDestroyWindow(window);
|
||||
const dev = try Device.init(ally, &inst, &win);
|
||||
defer dev.deinit();
|
||||
|
||||
const vkb = try gfx.BaseDispatch.load(c.glfwGetInstanceProcAddress);
|
||||
var sc = try Swapchain.create(ally, &dev);
|
||||
defer sc.deinit();
|
||||
|
||||
const instance, const vki, const messenger = try gfx.create_instance(vkb, app_name);
|
||||
defer vki.destroyInstance(instance, null);
|
||||
defer if (gfx.use_debug_messenger)
|
||||
vki.destroyDebugUtilsMessengerEXT(instance, messenger, null);
|
||||
const device_local = gfx.VkAllocator.init(dev.pdev, inst.vki);
|
||||
|
||||
const surface = try gfx.create_surface(instance, window);
|
||||
defer vki.destroySurfaceKHR(instance, surface, null);
|
||||
|
||||
const pdev: vk.PhysicalDevice, const dev: vk.Device, const vkd: gfx.DeviceDispatch, const family: u32 =
|
||||
try gfx.create_device(ally, instance, surface, vki);
|
||||
defer vkd.destroyDevice(dev, null);
|
||||
|
||||
const queue = vkd.getDeviceQueue(dev, family, 0);
|
||||
|
||||
const pool = try vkd.createCommandPool(dev, &.{
|
||||
.queue_family_index = family,
|
||||
}, null);
|
||||
defer vkd.destroyCommandPool(dev, pool, null);
|
||||
|
||||
const preferred_format: vk.SurfaceFormatKHR = .{
|
||||
.format = .b8g8r8a8_srgb,
|
||||
.color_space = .srgb_nonlinear_khr,
|
||||
};
|
||||
const format = try gfx.find_surface_format(pdev, vki, surface, preferred_format);
|
||||
|
||||
const present_mode = try gfx.find_present_mode(pdev, vki, surface, .mailbox_khr);
|
||||
|
||||
const swap_image_count = try gfx.find_swap_image_count(pdev, vki, surface);
|
||||
|
||||
var swapchain: vk.SwapchainKHR = .null_handle;
|
||||
defer vkd.destroySwapchainKHR(dev, swapchain, null);
|
||||
|
||||
var chain = std.MultiArrayList(ChainImage){};
|
||||
defer chain.deinit(ally);
|
||||
defer deinit_chain(chain, dev, vkd, pool);
|
||||
|
||||
const device_local = gfx.VkAllocator.init(pdev, vki);
|
||||
|
||||
const pipeline_layout = try vkd.createPipelineLayout(dev, &.{
|
||||
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 vkd.destroyPipelineLayout(dev, pipeline_layout, null);
|
||||
defer dev.vkd.destroyPipelineLayout(dev.dev, pipeline_layout, null);
|
||||
|
||||
const pipeline = try createPipeline(dev, pipeline_layout, format, vkd);
|
||||
defer vkd.destroyPipeline(dev, pipeline, 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 vkd.createBuffer(dev, &.{
|
||||
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 vkd.destroyBuffer(dev, vertex_buffer, null);
|
||||
const vertex_mem_reqs = vkd.getBufferMemoryRequirements(dev, vertex_buffer);
|
||||
// const vertex_memory = try gfx.allocate(pdev, vki, dev, vkd, vertex_mem_reqs, .{ .device_local_bit = true });
|
||||
const vertex_memory = try device_local.alloc(dev, vkd, vertex_mem_reqs, .{ .device_local_bit = true });
|
||||
defer vkd.freeMemory(dev, vertex_memory, null);
|
||||
try vkd.bindBufferMemory(dev, vertex_buffer, vertex_memory, 0);
|
||||
defer dev.vkd.destroyBuffer(dev.dev, vertex_buffer, null);
|
||||
const vertex_mem_reqs = dev.vkd.getBufferMemoryRequirements(dev.dev, vertex_buffer);
|
||||
// const vertex_memory = try gfx.allocate(pdev, vki, dev, dev.vkd, vertex_mem_reqs, .{ .device_local_bit = true });
|
||||
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, pdev, vki, dev, vkd, queue, pool, vertex_buffer, &vertices);
|
||||
try gfx.uploadData(Vertex, dev.pdev, inst.vki, dev.dev, dev.vkd, dev.queue, dev.pool, vertex_buffer, &vertices);
|
||||
|
||||
const index_buffer = try vkd.createBuffer(dev, &.{
|
||||
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 vkd.destroyBuffer(dev, index_buffer, null);
|
||||
const index_mem_reqs = vkd.getBufferMemoryRequirements(dev, index_buffer);
|
||||
// const index_memory = try gfx.allocate(pdev, vki, dev, vkd, index_mem_reqs, .{ .device_local_bit = true });
|
||||
const index_memory = try device_local.alloc(dev, vkd, index_mem_reqs, .{ .device_local_bit = true });
|
||||
defer vkd.freeMemory(dev, index_memory, null);
|
||||
try vkd.bindBufferMemory(dev, index_buffer, index_memory, 0);
|
||||
defer dev.vkd.destroyBuffer(dev.dev, index_buffer, null);
|
||||
const index_mem_reqs = dev.vkd.getBufferMemoryRequirements(dev.dev, index_buffer);
|
||||
// const index_memory = try gfx.allocate(pdev, vki, dev, dev.vkd, index_mem_reqs, .{ .device_local_bit = true });
|
||||
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, pdev, vki, dev, vkd, queue, pool, index_buffer, &indices);
|
||||
try gfx.uploadData(Index, dev.pdev, inst.vki, dev.dev, dev.vkd, dev.queue, dev.pool, index_buffer, &indices);
|
||||
|
||||
extent = try create_swapchain(
|
||||
&chain,
|
||||
&swapchain,
|
||||
ally,
|
||||
pdev,
|
||||
vki,
|
||||
window,
|
||||
dev,
|
||||
vkd,
|
||||
pool,
|
||||
surface,
|
||||
swap_image_count,
|
||||
format,
|
||||
present_mode,
|
||||
);
|
||||
|
||||
for (chain.items(.image), chain.items(.view), chain.items(.cmdbuf)) |image, view, cmdbuf| {
|
||||
try record_cmdbuf(cmdbuf, vkd, image, view, extent, pipeline, vertex_buffer, index_buffer);
|
||||
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(window) == c.GLFW_FALSE) {
|
||||
while (c.glfwWindowShouldClose(win.ref) == c.GLFW_FALSE) {
|
||||
var w: c_int = undefined;
|
||||
var h: c_int = undefined;
|
||||
c.glfwGetFramebufferSize(window, &w, &h);
|
||||
c.glfwGetFramebufferSize(win.ref, &w, &h);
|
||||
|
||||
// Don't present or resize swapchain while the window is minimized
|
||||
if (w == 0 or h == 0) {
|
||||
@@ -320,32 +267,15 @@ pub fn main() !void {
|
||||
continue;
|
||||
}
|
||||
|
||||
const frame: ChainImage = chain.get(index);
|
||||
const frame = sc.chain.get(index);
|
||||
|
||||
render(dev, vkd, swapchain, frame, queue) catch |err| switch (err) {
|
||||
render(dev.dev, dev.vkd, sc.ref, frame, dev.queue) catch |err| switch (err) {
|
||||
error.OutOfDateKHR => {
|
||||
try vkd.deviceWaitIdle(dev);
|
||||
try dev.vkd.deviceWaitIdle(dev.dev);
|
||||
|
||||
deinit_chain(chain, dev, vkd, pool);
|
||||
|
||||
extent = try create_swapchain(
|
||||
&chain,
|
||||
&swapchain,
|
||||
ally,
|
||||
pdev,
|
||||
vki,
|
||||
window,
|
||||
dev,
|
||||
vkd,
|
||||
pool,
|
||||
surface,
|
||||
swap_image_count,
|
||||
format,
|
||||
present_mode,
|
||||
);
|
||||
|
||||
for (chain.items(.image), chain.items(.view), chain.items(.cmdbuf)) |image, view, cmdbuf| {
|
||||
try record_cmdbuf(cmdbuf, vkd, image, view, extent, pipeline, vertex_buffer, index_buffer);
|
||||
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;
|
||||
@@ -357,15 +287,15 @@ pub fn main() !void {
|
||||
|
||||
c.glfwPollEvents();
|
||||
|
||||
index = @intCast((index + 1) % chain.len);
|
||||
index = @intCast((index + 1) % sc.chain.len);
|
||||
}
|
||||
|
||||
try vkd.deviceWaitIdle(dev);
|
||||
try dev.vkd.deviceWaitIdle(dev.dev);
|
||||
}
|
||||
|
||||
fn record_cmdbuf(
|
||||
cmdbuf: vk.CommandBuffer,
|
||||
vkd: gfx.DeviceDispatch,
|
||||
vkd: Device.Wrapper,
|
||||
image: vk.Image,
|
||||
view: vk.ImageView,
|
||||
extent: vk.Extent2D,
|
||||
@@ -486,7 +416,7 @@ fn record_cmdbuf(
|
||||
try vkd.endCommandBuffer(cmdbuf);
|
||||
}
|
||||
|
||||
fn createPipeline(dev: vk.Device, layout: vk.PipelineLayout, format: vk.SurfaceFormatKHR, vkd: gfx.DeviceDispatch) !vk.Pipeline {
|
||||
fn createPipeline(dev: vk.Device, layout: vk.PipelineLayout, format: vk.SurfaceFormatKHR, vkd: Device.Wrapper) !vk.Pipeline {
|
||||
const vert = try vkd.createShaderModule(dev, &.{
|
||||
.code_size = shaders.triangle_vert.len,
|
||||
.p_code = @as([*]const u32, @ptrCast(&shaders.triangle_vert)),
|
||||
|
Reference in New Issue
Block a user