it compiles, but initialization is not done create debug messenger Use proxies wip swapchain wip swapchain - stub usage get device queue wip swapchain - scaffolded with segfault wip swapchain - fix segfault wip swapchain - working, but resize broken. semaphore issue with naive handling satisfy validation
299 lines
9.0 KiB
Zig
299 lines
9.0 KiB
Zig
const std = @import("std");
|
|
const builtin = @import("builtin");
|
|
const vk = @import("vk");
|
|
const nu = @import("../../nu.zig");
|
|
|
|
const Debug = @import("Debug.zig");
|
|
|
|
const config = nu.config.render;
|
|
|
|
pub const versions: []const vk.ApiInfo = &.{
|
|
vk.features.version_1_0,
|
|
vk.features.version_1_1,
|
|
vk.features.version_1_2,
|
|
vk.features.version_1_3,
|
|
};
|
|
|
|
pub const instance_exts: []const vk.ApiInfo = if (config.use_debug_messenger) &.{
|
|
vk.extensions.ext_debug_utils,
|
|
vk.extensions.khr_surface,
|
|
} else &.{};
|
|
|
|
pub const device_exts: []const vk.ApiInfo = &.{
|
|
vk.extensions.khr_swapchain,
|
|
vk.extensions.khr_dynamic_rendering,
|
|
vk.extensions.khr_timeline_semaphore,
|
|
};
|
|
|
|
pub const apis = versions ++ instance_exts ++ device_exts;
|
|
|
|
pub const layers: []const [*:0]const u8 =
|
|
if (config.use_debug_messenger) &.{
|
|
"VK_LAYER_KHRONOS_validation",
|
|
} else &.{};
|
|
|
|
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);
|
|
|
|
var _iw: InstanceWrapper = undefined;
|
|
pub const iw: *const InstanceWrapper = &_iw;
|
|
var _dw: DeviceWrapper = undefined;
|
|
pub const dw: *const DeviceWrapper = &_dw;
|
|
|
|
var _B: BaseWrapper = undefined;
|
|
pub const B: *const BaseWrapper = &_B;
|
|
var _I: InstanceProxy = undefined;
|
|
pub const I: *const InstanceProxy = &_I;
|
|
var _D: DeviceProxy = undefined;
|
|
pub const D: *const DeviceProxy = &_D;
|
|
var _Q: QueueProxy = undefined;
|
|
pub const Q: *const QueueProxy = &_Q;
|
|
|
|
var _instance: vk.Instance = undefined;
|
|
pub const instance: *const vk.Instance = &_instance;
|
|
var _messenger: if (config.use_debug_messenger) vk.DebugUtilsMessengerEXT else void = undefined;
|
|
pub const messenger: *const if (config.use_debug_messenger) vk.DebugUtilsMessengerEXT else void = &_messenger;
|
|
var _surface: vk.SurfaceKHR = undefined;
|
|
pub const surface: *const vk.SurfaceKHR = &_surface;
|
|
var _pdevice: vk.PhysicalDevice = undefined;
|
|
pub const pdevice: *const vk.PhysicalDevice = &_pdevice;
|
|
var _device: vk.Device = undefined;
|
|
pub const device: *const vk.Device = &_device;
|
|
var _family: u32 = undefined;
|
|
pub const family: *const u32 = &_family;
|
|
var _queue: vk.Queue = undefined;
|
|
pub const queue: *const vk.Queue = &_queue;
|
|
|
|
pub fn init(alloc: std.mem.Allocator) !void {
|
|
_B = try BaseWrapper.load(glfwGetInstanceProcAddress);
|
|
|
|
_instance = try _create_instance(alloc);
|
|
_iw = try InstanceWrapper.load(_instance, glfwGetInstanceProcAddress);
|
|
errdefer _destroy_instance();
|
|
_I = InstanceProxy.init(_instance, iw);
|
|
|
|
if (config.use_debug_messenger) _messenger = try _create_messenger();
|
|
errdefer if (config.use_debug_messenger) _destroy_messenger();
|
|
|
|
_surface = try _create_surface();
|
|
errdefer _destroy_surface();
|
|
|
|
_pdevice = try _select_pdevice(alloc);
|
|
_family = try _select_queue_family_index(alloc); // only one queue supported
|
|
_device = try _create_device(alloc);
|
|
_dw = try DeviceWrapper.load(_device, iw.dispatch.vkGetDeviceProcAddr);
|
|
errdefer _destroy_device();
|
|
_D = DeviceProxy.init(_device, dw);
|
|
_queue = D.getDeviceQueue(_family, 0); // only one queue supported
|
|
_Q = QueueProxy.init(_queue, dw);
|
|
}
|
|
|
|
pub fn deinit() void {
|
|
_destroy_device();
|
|
_destroy_surface();
|
|
if (config.use_debug_messenger) _destroy_messenger();
|
|
_destroy_instance();
|
|
}
|
|
|
|
fn _create_instance(alloc: std.mem.Allocator) !vk.Instance {
|
|
var extnames = std.ArrayList([*:0]const u8).init(alloc);
|
|
defer extnames.deinit();
|
|
|
|
for (instance_exts) |ext|
|
|
try extnames.append(ext.name);
|
|
|
|
var glfw_exts_count: u32 = 0;
|
|
const glfw_exts: [*]const [*:0]const u8 =
|
|
glfwGetRequiredInstanceExtensions(&glfw_exts_count);
|
|
try extnames.appendSlice(glfw_exts[0..glfw_exts_count]);
|
|
|
|
var ci: vk.InstanceCreateInfo = .{
|
|
.p_application_info = &vk.ApplicationInfo{
|
|
.p_application_name = config.app_name,
|
|
.application_version = vk.makeApiVersion(
|
|
config.app_version.variant,
|
|
config.app_version.major,
|
|
config.app_version.minor,
|
|
config.app_version.patch,
|
|
),
|
|
.p_engine_name = config.engine_name,
|
|
.engine_version = vk.makeApiVersion(
|
|
config.engine_version.variant,
|
|
config.engine_version.major,
|
|
config.engine_version.minor,
|
|
config.engine_version.patch,
|
|
),
|
|
.api_version = vk.features.version_1_3.version,
|
|
},
|
|
.enabled_extension_count = @intCast(extnames.items.len),
|
|
.pp_enabled_extension_names = extnames.items.ptr,
|
|
.enabled_layer_count = @intCast(layers.len),
|
|
.pp_enabled_layer_names = layers.ptr,
|
|
};
|
|
|
|
if (config.use_debug_messenger) ci.p_next = &Debug.ci;
|
|
|
|
return try B.createInstance(&ci, null);
|
|
}
|
|
|
|
fn _destroy_instance() void {
|
|
I.destroyInstance(null);
|
|
}
|
|
|
|
fn _create_messenger() !vk.DebugUtilsMessengerEXT {
|
|
return try I.createDebugUtilsMessengerEXT(&Debug.ci, null);
|
|
}
|
|
|
|
fn _destroy_messenger() void {
|
|
I.destroyDebugUtilsMessengerEXT(_messenger, null);
|
|
}
|
|
|
|
fn _create_surface() !vk.SurfaceKHR {
|
|
var res: vk.SurfaceKHR = undefined;
|
|
if (glfwCreateWindowSurface(
|
|
_instance,
|
|
nu.Window.handle,
|
|
null,
|
|
&res,
|
|
) != .success) {
|
|
return error.CreateWindowSurfaceFailed;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
fn _destroy_surface() void {
|
|
I.destroySurfaceKHR(_surface, null);
|
|
}
|
|
|
|
fn _select_pdevice(alloc: std.mem.Allocator) !vk.PhysicalDevice {
|
|
var count: u32 = undefined;
|
|
_ = try I.enumeratePhysicalDevices(
|
|
&count,
|
|
null,
|
|
);
|
|
const pdevs = try alloc.alloc(vk.PhysicalDevice, count);
|
|
defer alloc.free(pdevs);
|
|
_ = try I.enumeratePhysicalDevices(
|
|
&count,
|
|
pdevs.ptr,
|
|
);
|
|
|
|
const scores = try alloc.alloc(i32, count);
|
|
@memset(scores, 0);
|
|
defer alloc.free(scores);
|
|
|
|
for (pdevs, scores) |pdev, *score| {
|
|
const props = I.getPhysicalDeviceProperties(pdev);
|
|
score.* += switch (props.device_type) {
|
|
.discrete_gpu => 1000,
|
|
.integrated_gpu => 500,
|
|
else => 0,
|
|
};
|
|
}
|
|
|
|
const idx = std.mem.indexOfMax(i32, scores);
|
|
|
|
return pdevs[idx];
|
|
}
|
|
|
|
fn _select_queue_family_index(alloc: std.mem.Allocator) !u32 {
|
|
var count: u32 = undefined;
|
|
I.getPhysicalDeviceQueueFamilyProperties(
|
|
_pdevice,
|
|
&count,
|
|
null,
|
|
);
|
|
const families = try alloc.alloc(vk.QueueFamilyProperties, count);
|
|
defer alloc.free(families);
|
|
I.getPhysicalDeviceQueueFamilyProperties(
|
|
_pdevice,
|
|
&count,
|
|
families.ptr,
|
|
);
|
|
|
|
for (families, 0..) |prop, idx| {
|
|
if (!prop.queue_flags.graphics_bit) continue;
|
|
|
|
if (!prop.queue_flags.transfer_bit) continue;
|
|
|
|
if (try I.getPhysicalDeviceSurfaceSupportKHR(
|
|
_pdevice,
|
|
@intCast(idx),
|
|
_surface,
|
|
) != vk.TRUE) continue;
|
|
|
|
return @intCast(idx);
|
|
}
|
|
|
|
return error.NoSuitableQueueFamily;
|
|
}
|
|
|
|
fn _create_device(alloc: std.mem.Allocator) !vk.Device {
|
|
const qci: []const vk.DeviceQueueCreateInfo = &.{
|
|
vk.DeviceQueueCreateInfo{
|
|
.queue_count = 1,
|
|
.queue_family_index = @intCast(_family),
|
|
.p_queue_priorities = &[_]f32{1.0},
|
|
},
|
|
};
|
|
|
|
var extnames = std.ArrayList([*:0]const u8).init(alloc);
|
|
defer extnames.deinit();
|
|
|
|
for (device_exts) |ext|
|
|
try extnames.append(ext.name);
|
|
|
|
const ci: vk.DeviceCreateInfo = .{
|
|
.queue_create_info_count = @intCast(qci.len),
|
|
.p_queue_create_infos = qci.ptr,
|
|
.enabled_extension_count = @intCast(extnames.items.len),
|
|
.pp_enabled_extension_names = extnames.items.ptr,
|
|
.p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{
|
|
.dynamic_rendering = vk.TRUE,
|
|
},
|
|
};
|
|
|
|
return try I.createDevice(_pdevice, &ci, null);
|
|
}
|
|
|
|
fn _destroy_device() void {
|
|
D.deviceWaitIdle() catch |err| switch (err) {
|
|
error.OutOfHostMemory,
|
|
error.OutOfDeviceMemory,
|
|
error.DeviceLost,
|
|
=> {
|
|
// In these cases we would destroy the device anyway, so just fall through. Note any child objects must
|
|
// already be destroyed. This assumes normal cleanup has been done before _destroy_device was called.
|
|
},
|
|
else => unreachable,
|
|
};
|
|
D.destroyDevice(null);
|
|
}
|
|
|
|
pub extern fn glfwGetInstanceProcAddress(
|
|
instance: vk.Instance,
|
|
procname: [*:0]const u8,
|
|
) vk.PfnVoidFunction;
|
|
|
|
pub extern fn glfwGetPhysicalDevicePresentationSupport(
|
|
instance: vk.Instance,
|
|
pdev: vk.PhysicalDevice,
|
|
queuefamily: u32,
|
|
) c_int;
|
|
|
|
pub extern fn glfwCreateWindowSurface(
|
|
instance: vk.Instance,
|
|
window: *nu.Window.c.GLFWwindow,
|
|
allocation_callbacks: ?*const vk.AllocationCallbacks,
|
|
surface: *vk.SurfaceKHR,
|
|
) vk.Result;
|
|
|
|
pub extern fn glfwGetRequiredInstanceExtensions(
|
|
count: *u32,
|
|
) [*][*:0]const u8;
|