Remove old au work
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const au = @import("au.zig");
|
|
||||||
const vk = @import("vk");
|
const vk = @import("vk");
|
||||||
const shaders = @import("shaders");
|
const shaders = @import("shaders");
|
||||||
|
|
||||||
|
@@ -1,395 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const builtin = @import("builtin");
|
|
||||||
|
|
||||||
const vk = @import("vk");
|
|
||||||
const nu = @import("../../nu.zig");
|
|
||||||
|
|
||||||
pub const SwapChain = @import("au/SwapChain.zig");
|
|
||||||
pub const Flights = @import("au/Flights.zig");
|
|
||||||
pub const VkAllocator = @import("au/VkAllocator.zig");
|
|
||||||
|
|
||||||
const config = nu.config.render;
|
|
||||||
|
|
||||||
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 (config.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 BaseWrapper = vk.BaseWrapper(apis);
|
|
||||||
pub const InstanceWrapper = vk.InstanceWrapper(apis);
|
|
||||||
pub const DeviceWrapper = vk.DeviceWrapper(apis);
|
|
||||||
|
|
||||||
pub const InstanceProxy = vk.InstanceProxy(apis);
|
|
||||||
pub const DeviceProxy = vk.DeviceProxy(apis);
|
|
||||||
pub const QueueProxy = vk.QueueProxy(apis);
|
|
||||||
pub const CommandBufferProxy = vk.CommandBufferProxy(apis);
|
|
||||||
|
|
||||||
pub const B: *const BaseWrapper = &_bw;
|
|
||||||
pub const I: *const InstanceProxy = &_ip;
|
|
||||||
pub const D: *const DeviceProxy = &_dp;
|
|
||||||
pub const Q: *const QueueProxy = &_qp;
|
|
||||||
pub const S: *const vk.SurfaceKHR = &_surface;
|
|
||||||
|
|
||||||
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 _surface: vk.SurfaceKHR = 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_base();
|
|
||||||
errdefer deinit_base();
|
|
||||||
|
|
||||||
try init_instance(alloc);
|
|
||||||
errdefer deinit_instance();
|
|
||||||
|
|
||||||
try init_device(alloc);
|
|
||||||
errdefer deinit_device();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit() void {
|
|
||||||
deinit_device();
|
|
||||||
deinit_instance();
|
|
||||||
deinit_base();
|
|
||||||
}
|
|
||||||
|
|
||||||
fn init_base() !void {
|
|
||||||
if (glfwVulkanSupported() != nu.Window.c.GLFW_TRUE)
|
|
||||||
return error.glfwNoVulkan;
|
|
||||||
|
|
||||||
if (config.use_debug_messenger) {
|
|
||||||
_bw = try BaseWrapper.load(glfwGetInstanceProcAddress);
|
|
||||||
} else {
|
|
||||||
_bw = BaseWrapper.loadNoFail(glfwGetInstanceProcAddress);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit_base() void {}
|
|
||||||
|
|
||||||
fn init_instance(alloc: std.mem.Allocator) !void {
|
|
||||||
var extensions = std.ArrayList([*:0]const u8).init(alloc);
|
|
||||||
defer extensions.deinit();
|
|
||||||
|
|
||||||
var layers = std.ArrayList([*:0]const u8).init(alloc);
|
|
||||||
defer layers.deinit();
|
|
||||||
|
|
||||||
if (config.use_debug_messenger) {
|
|
||||||
try extensions.appendSlice(&.{
|
|
||||||
vk.extensions.ext_debug_utils.name,
|
|
||||||
});
|
|
||||||
|
|
||||||
try layers.appendSlice(&.{
|
|
||||||
"VK_LAYER_KHRONOS_validation",
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var glfw_exts_count: u32 = 0;
|
|
||||||
const glfw_exts: [*]const [*:0]const u8 =
|
|
||||||
@ptrCast(glfwGetRequiredInstanceExtensions(&glfw_exts_count));
|
|
||||||
try extensions.appendSlice(glfw_exts[0..glfw_exts_count]);
|
|
||||||
|
|
||||||
const mci: vk.DebugUtilsMessengerCreateInfoEXT = .{
|
|
||||||
.message_severity = .{
|
|
||||||
.error_bit_ext = true,
|
|
||||||
.info_bit_ext = true,
|
|
||||||
.verbose_bit_ext = true,
|
|
||||||
.warning_bit_ext = true,
|
|
||||||
},
|
|
||||||
.message_type = .{
|
|
||||||
.device_address_binding_bit_ext = true,
|
|
||||||
.general_bit_ext = false,
|
|
||||||
.performance_bit_ext = true,
|
|
||||||
.validation_bit_ext = true,
|
|
||||||
},
|
|
||||||
.pfn_user_callback = &debug_callback,
|
|
||||||
.p_user_data = null,
|
|
||||||
};
|
|
||||||
|
|
||||||
_instance = try B.createInstance(&.{
|
|
||||||
.p_application_info = &.{
|
|
||||||
.p_application_name = 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.API_VERSION_1_3,
|
|
||||||
},
|
|
||||||
.enabled_extension_count = @intCast(extensions.items.len),
|
|
||||||
.pp_enabled_extension_names = extensions.items.ptr,
|
|
||||||
.enabled_layer_count = @intCast(layers.items.len),
|
|
||||||
.pp_enabled_layer_names = layers.items.ptr,
|
|
||||||
.p_next = if (config.use_debug_messenger) &mci else null,
|
|
||||||
}, null);
|
|
||||||
|
|
||||||
if (config.use_debug_messenger) {
|
|
||||||
_iw = try InstanceWrapper.load(_instance, _bw.dispatch.vkGetInstanceProcAddr);
|
|
||||||
} else {
|
|
||||||
_iw = InstanceWrapper.loadNoFail(_instance, _bw.dispatch.vkGetInstanceProcAddr);
|
|
||||||
}
|
|
||||||
|
|
||||||
_ip = InstanceProxy.init(_instance, &_iw);
|
|
||||||
|
|
||||||
if (glfwCreateWindowSurface(_instance, nu.Window.handle, null, &_surface) != .success) {
|
|
||||||
return error.glfwCreateWindowSurfaceFailed;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn deinit_instance() void {
|
|
||||||
_ip.destroySurfaceKHR(_surface, null);
|
|
||||||
_ip.destroyInstance(null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const CandidateDeviceInfo = struct {
|
|
||||||
pdev: vk.PhysicalDevice,
|
|
||||||
format: vk.SurfaceFormatKHR,
|
|
||||||
mode: vk.PresentModeKHR,
|
|
||||||
family: u32, // must support graphics and present for now
|
|
||||||
|
|
||||||
fn init(alloc: std.mem.Allocator, pdev: vk.PhysicalDevice) !struct { i32, CandidateDeviceInfo } {
|
|
||||||
var score: i32 = 0;
|
|
||||||
var res: CandidateDeviceInfo = undefined;
|
|
||||||
|
|
||||||
res.pdev = pdev;
|
|
||||||
|
|
||||||
const props = I.getPhysicalDeviceProperties(pdev);
|
|
||||||
score += switch (props.device_type) {
|
|
||||||
vk.PhysicalDeviceType.discrete_gpu => 1000,
|
|
||||||
vk.PhysicalDeviceType.integrated_gpu => 500,
|
|
||||||
else => 0,
|
|
||||||
};
|
|
||||||
|
|
||||||
var format_count: u32 = undefined;
|
|
||||||
_ = try I.getPhysicalDeviceSurfaceFormatsKHR(pdev, _surface, &format_count, null);
|
|
||||||
if (format_count == 0) return error.NoSurfaceFormats;
|
|
||||||
const formats = try alloc.alloc(vk.SurfaceFormatKHR, format_count);
|
|
||||||
defer alloc.free(formats);
|
|
||||||
_ = try I.getPhysicalDeviceSurfaceFormatsKHR(pdev, _surface, &format_count, formats.ptr);
|
|
||||||
|
|
||||||
for (formats) |fmt| {
|
|
||||||
if (fmt.color_space == .srgb_nonlinear_khr) {
|
|
||||||
res.format = fmt;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
res.format = formats[0];
|
|
||||||
score -= 100;
|
|
||||||
}
|
|
||||||
|
|
||||||
var mode_count: u32 = undefined;
|
|
||||||
_ = try I.getPhysicalDeviceSurfacePresentModesKHR(pdev, _surface, &mode_count, null);
|
|
||||||
if (mode_count == 0) return error.NoSurfacePresentModes;
|
|
||||||
const modes = try alloc.alloc(vk.PresentModeKHR, mode_count);
|
|
||||||
defer alloc.free(modes);
|
|
||||||
_ = try I.getPhysicalDeviceSurfacePresentModesKHR(pdev, _surface, &mode_count, modes.ptr);
|
|
||||||
|
|
||||||
if (std.mem.indexOfAny(vk.PresentModeKHR, modes, &.{
|
|
||||||
vk.PresentModeKHR.mailbox_khr,
|
|
||||||
vk.PresentModeKHR.immediate_khr,
|
|
||||||
})) |idx| {
|
|
||||||
res.mode = modes[idx];
|
|
||||||
} else {
|
|
||||||
score -= 50;
|
|
||||||
res.mode = .fifo_khr; // this is guaranteed
|
|
||||||
}
|
|
||||||
|
|
||||||
var ext_count: u32 = undefined;
|
|
||||||
_ = try I.enumerateDeviceExtensionProperties(pdev, null, &ext_count, null);
|
|
||||||
const exts = try alloc.alloc(vk.ExtensionProperties, ext_count);
|
|
||||||
defer alloc.free(exts);
|
|
||||||
_ = try I.enumerateDeviceExtensionProperties(pdev, null, &ext_count, exts.ptr);
|
|
||||||
|
|
||||||
for (device_extensions) |needle| {
|
|
||||||
for (exts) |ext| {
|
|
||||||
if (std.mem.eql(
|
|
||||||
u8,
|
|
||||||
std.mem.span(needle),
|
|
||||||
std.mem.sliceTo(&ext.extension_name, 0),
|
|
||||||
))
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
return error.MissingDeviceExtension;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var family_count: u32 = undefined;
|
|
||||||
I.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, null);
|
|
||||||
const families = try alloc.alloc(vk.QueueFamilyProperties, family_count);
|
|
||||||
defer alloc.free(families);
|
|
||||||
I.getPhysicalDeviceQueueFamilyProperties(pdev, &family_count, families.ptr);
|
|
||||||
|
|
||||||
for (families, 0..) |prop, idx| {
|
|
||||||
const graphics_support = prop.queue_flags.graphics_bit;
|
|
||||||
const present_support = try I.getPhysicalDeviceSurfaceSupportKHR(pdev, @intCast(idx), _surface) == vk.TRUE;
|
|
||||||
|
|
||||||
if (graphics_support and present_support) {
|
|
||||||
res.family = @intCast(idx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return error.NoSuitableFamily;
|
|
||||||
}
|
|
||||||
|
|
||||||
return .{ score, res };
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
fn init_device(alloc: std.mem.Allocator) !void {
|
|
||||||
var pdev_count: u32 = undefined;
|
|
||||||
_ = try I.enumeratePhysicalDevices(&pdev_count, null);
|
|
||||||
if (pdev_count == 0) return error.NoDevice;
|
|
||||||
const pdevs = try alloc.alloc(vk.PhysicalDevice, pdev_count);
|
|
||||||
defer alloc.free(pdevs);
|
|
||||||
_ = try I.enumeratePhysicalDevices(&pdev_count, pdevs.ptr);
|
|
||||||
|
|
||||||
// const scores = std.ArrayList(i32).
|
|
||||||
var scores: std.MultiArrayList(struct { score: i32, ci: CandidateDeviceInfo }) = .{};
|
|
||||||
defer scores.deinit(alloc);
|
|
||||||
|
|
||||||
for (pdevs) |pdev| {
|
|
||||||
const score, const ci = CandidateDeviceInfo.init(alloc, pdev) catch continue;
|
|
||||||
try scores.append(alloc, .{ .score = score, .ci = ci });
|
|
||||||
}
|
|
||||||
|
|
||||||
const idx = std.sort.argMax(i32, scores.items(.score), {}, std.sort.asc(i32)) orelse
|
|
||||||
return error.NoSuitableDevice;
|
|
||||||
_dconfig = scores.get(idx).ci;
|
|
||||||
|
|
||||||
const qci: []const vk.DeviceQueueCreateInfo = &.{
|
|
||||||
vk.DeviceQueueCreateInfo{
|
|
||||||
.queue_family_index = _dconfig.family,
|
|
||||||
.queue_count = 1,
|
|
||||||
.p_queue_priorities = &[_]f32{1.0},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
_device = try I.createDevice(_dconfig.pdev, &.{
|
|
||||||
.queue_create_info_count = @intCast(qci.len),
|
|
||||||
.p_queue_create_infos = qci.ptr,
|
|
||||||
.enabled_extension_count = @intCast(device_extensions.len),
|
|
||||||
.pp_enabled_extension_names = device_extensions.ptr,
|
|
||||||
.p_next = &vk.PhysicalDeviceDynamicRenderingFeaturesKHR{
|
|
||||||
.dynamic_rendering = vk.TRUE,
|
|
||||||
},
|
|
||||||
}, null);
|
|
||||||
|
|
||||||
if (config.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub extern fn glfwVulkanSupported() c_int;
|
|
||||||
|
|
||||||
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;
|
|
@@ -1,62 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const vk = @import("vk");
|
|
||||||
const au = @import("../au.zig");
|
|
||||||
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub const Flight = struct {
|
|
||||||
acquire: vk.Semaphore = .null_handle,
|
|
||||||
complete: vk.Semaphore = .null_handle,
|
|
||||||
fence: vk.Fence = .null_handle,
|
|
||||||
pool: vk.CommandPool = .null_handle,
|
|
||||||
cmd: vk.CommandBuffer = .null_handle,
|
|
||||||
|
|
||||||
pub fn wait(self: Flight) !void {
|
|
||||||
_ = try au.D.waitForFences(1, &.{self.fence}, vk.TRUE, std.math.maxInt(u64));
|
|
||||||
try au.D.resetFences(1, &.{self.fence});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
alloc: std.mem.Allocator,
|
|
||||||
flights: []Flight,
|
|
||||||
idx: usize,
|
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, n: usize) !Self {
|
|
||||||
var self: Self = .{
|
|
||||||
.alloc = alloc,
|
|
||||||
.flights = try alloc.alloc(Flight, n),
|
|
||||||
.idx = 0,
|
|
||||||
};
|
|
||||||
errdefer self.deinit();
|
|
||||||
|
|
||||||
for (self.flights) |*flight| {
|
|
||||||
flight.acquire = try au.D.createSemaphore(&.{}, null);
|
|
||||||
flight.complete = try au.D.createSemaphore(&.{}, null);
|
|
||||||
flight.fence = try au.D.createFence(&.{ .flags = .{ .signaled_bit = true } }, null);
|
|
||||||
flight.pool = try au.D.createCommandPool(&.{ .queue_family_index = au.device_config.family }, null);
|
|
||||||
try au.D.allocateCommandBuffers(&vk.CommandBufferAllocateInfo{
|
|
||||||
.command_buffer_count = 1,
|
|
||||||
.command_pool = flight.pool,
|
|
||||||
.level = .primary,
|
|
||||||
}, @ptrCast(&flight.cmd));
|
|
||||||
}
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: Self) void {
|
|
||||||
for (self.flights) |flight| {
|
|
||||||
au.D.destroySemaphore(flight.acquire, null);
|
|
||||||
au.D.destroySemaphore(flight.complete, null);
|
|
||||||
au.D.destroyFence(flight.fence, null);
|
|
||||||
au.D.freeCommandBuffers(flight.pool, 1, &.{flight.cmd});
|
|
||||||
au.D.destroyCommandPool(flight.pool, null);
|
|
||||||
}
|
|
||||||
self.alloc.free(self.flights);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn next(self: *Self) Flight {
|
|
||||||
const idx = self.idx;
|
|
||||||
self.idx = (self.idx + 1) % self.flights.len;
|
|
||||||
return self.flights[idx];
|
|
||||||
}
|
|
@@ -1,3 +1,5 @@
|
|||||||
|
// todo look into Vulkan Memory Allocator
|
||||||
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const vk = @import("vk");
|
const vk = @import("vk");
|
||||||
const au = @import("../au.zig");
|
const au = @import("../au.zig");
|
||||||
|
@@ -1,283 +0,0 @@
|
|||||||
const std = @import("std");
|
|
||||||
const vk = @import("vk");
|
|
||||||
const ctx = @import("ctx.zig");
|
|
||||||
|
|
||||||
fn _choose_format(alloc: std.mem.Allocator) !vk.SurfaceFormatKHR {
|
|
||||||
var count: u32 = undefined;
|
|
||||||
std.debug.assert(.success == try ctx.I.getPhysicalDeviceSurfaceFormatsKHR(
|
|
||||||
ctx.pdevice.*,
|
|
||||||
ctx.surface.*,
|
|
||||||
&count,
|
|
||||||
null,
|
|
||||||
));
|
|
||||||
const formats = try alloc.alloc(vk.SurfaceFormatKHR, count);
|
|
||||||
defer alloc.free(formats);
|
|
||||||
std.debug.assert(.success == try ctx.I.getPhysicalDeviceSurfaceFormatsKHR(
|
|
||||||
ctx.pdevice.*,
|
|
||||||
ctx.surface.*,
|
|
||||||
&count,
|
|
||||||
formats.ptr,
|
|
||||||
));
|
|
||||||
|
|
||||||
for (formats) |format| {
|
|
||||||
if (format.color_space == .srgb_nonlinear_khr) return format;
|
|
||||||
} else {
|
|
||||||
return formats[0];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn _choose_mode(alloc: std.mem.Allocator) !vk.PresentModeKHR {
|
|
||||||
_ = ctx;
|
|
||||||
_ = alloc;
|
|
||||||
|
|
||||||
return .fifo_khr;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn SwapChain(F: type) type {
|
|
||||||
return struct {
|
|
||||||
const Self = @This();
|
|
||||||
|
|
||||||
pub const Target = struct {
|
|
||||||
image_index: u32,
|
|
||||||
flight_index: u32,
|
|
||||||
image: vk.Image,
|
|
||||||
view: vk.ImageView,
|
|
||||||
flight: *F,
|
|
||||||
acquired: vk.Semaphore, // this semaphore will be signaled when the target is acquired
|
|
||||||
complete: vk.Semaphore, // this semaphore should be signaled when the render is complete
|
|
||||||
available: vk.Fence, // this fence should be signaled when the target flight is available
|
|
||||||
};
|
|
||||||
|
|
||||||
alloc: std.mem.Allocator,
|
|
||||||
|
|
||||||
cur: u8 = 0,
|
|
||||||
flights: []F,
|
|
||||||
acquired_sems: []vk.Semaphore,
|
|
||||||
complete_sems: []vk.Semaphore,
|
|
||||||
available_fncs: []vk.Fence,
|
|
||||||
|
|
||||||
cinfo: vk.SwapchainCreateInfoKHR,
|
|
||||||
handle: vk.SwapchainKHR,
|
|
||||||
|
|
||||||
images: std.ArrayListUnmanaged(vk.Image),
|
|
||||||
views: std.ArrayListUnmanaged(vk.ImageView),
|
|
||||||
|
|
||||||
pub fn init(alloc: std.mem.Allocator, flights: []F) !Self {
|
|
||||||
const acquired_sems = try alloc.alloc(vk.Semaphore, flights.len);
|
|
||||||
errdefer alloc.free(acquired_sems);
|
|
||||||
@memset(acquired_sems, .null_handle);
|
|
||||||
errdefer for (acquired_sems) |semaphore| ctx.D.destroySemaphore(semaphore, null);
|
|
||||||
for (acquired_sems) |*sem| {
|
|
||||||
sem.* = try ctx.D.createSemaphore(&vk.SemaphoreCreateInfo{}, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const complete_sems = try alloc.alloc(vk.Semaphore, flights.len);
|
|
||||||
errdefer alloc.free(complete_sems);
|
|
||||||
@memset(complete_sems, .null_handle);
|
|
||||||
errdefer for (complete_sems) |semaphore| ctx.D.destroySemaphore(semaphore, null);
|
|
||||||
for (complete_sems) |*sem| {
|
|
||||||
sem.* = try ctx.D.createSemaphore(&vk.SemaphoreCreateInfo{}, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const available_fncs = try alloc.alloc(vk.Fence, flights.len);
|
|
||||||
errdefer alloc.free(available_fncs);
|
|
||||||
@memset(available_fncs, .null_handle);
|
|
||||||
errdefer for (available_fncs) |fence| ctx.D.destroyFence(fence, null);
|
|
||||||
for (available_fncs) |*fnc| {
|
|
||||||
fnc.* = try ctx.D.createFence(&vk.FenceCreateInfo{ .flags = .{ .signaled_bit = true } }, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
const capabilities = try ctx.I.getPhysicalDeviceSurfaceCapabilitiesKHR(ctx.pdevice.*, ctx.surface.*);
|
|
||||||
const format = try _choose_format(alloc);
|
|
||||||
const mode = try _choose_mode(alloc);
|
|
||||||
|
|
||||||
var min_image_count = @min(3, capabilities.min_image_count + 1);
|
|
||||||
if (capabilities.max_image_count > 0) {
|
|
||||||
min_image_count = @min(min_image_count, capabilities.max_image_count);
|
|
||||||
}
|
|
||||||
|
|
||||||
const cinfo: vk.SwapchainCreateInfoKHR = .{
|
|
||||||
.surface = ctx.surface.*,
|
|
||||||
.min_image_count = min_image_count,
|
|
||||||
.image_format = format.format,
|
|
||||||
.image_color_space = format.color_space,
|
|
||||||
.image_extent = undefined, // set in rebuild
|
|
||||||
.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 = mode,
|
|
||||||
.clipped = vk.TRUE,
|
|
||||||
.old_swapchain = .null_handle,
|
|
||||||
};
|
|
||||||
|
|
||||||
return .{
|
|
||||||
.alloc = alloc,
|
|
||||||
.flights = flights,
|
|
||||||
.acquired_sems = acquired_sems,
|
|
||||||
.complete_sems = complete_sems,
|
|
||||||
.available_fncs = available_fncs,
|
|
||||||
.cinfo = cinfo,
|
|
||||||
.handle = .null_handle,
|
|
||||||
.images = .{},
|
|
||||||
.views = .{},
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
|
||||||
for (self.views.items) |view| ctx.D.destroyImageView(view, null);
|
|
||||||
self.views.deinit(self.alloc);
|
|
||||||
|
|
||||||
// images are owned by swapchain and not explicitly destroyed
|
|
||||||
self.images.deinit(self.alloc);
|
|
||||||
|
|
||||||
ctx.D.destroySwapchainKHR(self.handle, null);
|
|
||||||
|
|
||||||
// The easiest way to ensure fences and semaphores are not in use for deletion.
|
|
||||||
ctx.D.deviceWaitIdle() catch |err| switch (err) {
|
|
||||||
error.OutOfHostMemory,
|
|
||||||
error.OutOfDeviceMemory,
|
|
||||||
=> {},
|
|
||||||
error.DeviceLost,
|
|
||||||
=> return, // If the devices is lost there isn't much I know to do. I guess deinit is not needed?
|
|
||||||
else => unreachable,
|
|
||||||
};
|
|
||||||
|
|
||||||
for (self.available_fncs) |fnc| ctx.D.destroyFence(fnc, null);
|
|
||||||
self.alloc.free(self.available_fncs);
|
|
||||||
|
|
||||||
for (self.complete_sems) |sem| ctx.D.destroySemaphore(sem, null);
|
|
||||||
self.alloc.free(self.complete_sems);
|
|
||||||
|
|
||||||
for (self.acquired_sems) |sem| ctx.D.destroySemaphore(sem, null);
|
|
||||||
self.alloc.free(self.acquired_sems);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn acquire(self: *Self) !Target {
|
|
||||||
const flight_index = self.cur;
|
|
||||||
const acquired = self.acquired_sems[flight_index];
|
|
||||||
const complete = self.complete_sems[flight_index];
|
|
||||||
const available = self.available_fncs[flight_index];
|
|
||||||
|
|
||||||
const timeout = std.math.maxInt(u64);
|
|
||||||
|
|
||||||
for (0..5) |_| {
|
|
||||||
if (self.handle == .null_handle) {
|
|
||||||
try self.rebuild();
|
|
||||||
std.debug.assert(self.handle != .null_handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
const fences: [1]vk.Fence = .{available};
|
|
||||||
std.debug.assert(.success == try ctx.D.waitForFences(
|
|
||||||
1,
|
|
||||||
&fences,
|
|
||||||
vk.TRUE,
|
|
||||||
std.math.maxInt(u64),
|
|
||||||
));
|
|
||||||
|
|
||||||
if (ctx.D.acquireNextImageKHR(
|
|
||||||
self.handle,
|
|
||||||
timeout,
|
|
||||||
acquired,
|
|
||||||
.null_handle,
|
|
||||||
)) |res| {
|
|
||||||
switch (res.result) {
|
|
||||||
.success, .suboptimal_khr => {},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
|
|
||||||
try ctx.D.resetFences(1, &.{available});
|
|
||||||
self.cur = @intCast(@mod(self.cur + 1, self.flights.len));
|
|
||||||
|
|
||||||
return Target{
|
|
||||||
.image_index = res.image_index,
|
|
||||||
.flight_index = flight_index,
|
|
||||||
.image = self.images.items[res.image_index],
|
|
||||||
.view = self.views.items[res.image_index],
|
|
||||||
.flight = &self.flights[flight_index],
|
|
||||||
.acquired = acquired,
|
|
||||||
.complete = complete,
|
|
||||||
.available = available,
|
|
||||||
};
|
|
||||||
} else |err| switch (err) {
|
|
||||||
error.OutOfDateKHR => {
|
|
||||||
self.handle = .null_handle;
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
else => return err,
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return error.CannotRecreateSwapchain;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn present(self: *Self, target: Target) !void {
|
|
||||||
if (ctx.Q.presentKHR(&vk.PresentInfoKHR{
|
|
||||||
.wait_semaphore_count = 1, // todo extra semaphores?
|
|
||||||
.p_wait_semaphores = &.{target.complete},
|
|
||||||
.swapchain_count = 1,
|
|
||||||
.p_swapchains = &.{self.handle},
|
|
||||||
.p_image_indices = &.{target.image_index},
|
|
||||||
.p_results = null,
|
|
||||||
})) |res| {
|
|
||||||
switch (res) {
|
|
||||||
.success => {},
|
|
||||||
.suboptimal_khr => {
|
|
||||||
self.handle = .null_handle;
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
else => unreachable,
|
|
||||||
}
|
|
||||||
} else |err| switch (err) {
|
|
||||||
error.OutOfDateKHR => {
|
|
||||||
self.handle = .null_handle;
|
|
||||||
std.log.debug("Dropped frame", .{});
|
|
||||||
return;
|
|
||||||
},
|
|
||||||
else => return err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rebuild(self: *Self) !void {
|
|
||||||
std.debug.assert(self.handle == .null_handle);
|
|
||||||
|
|
||||||
const capabilities = try ctx.I.getPhysicalDeviceSurfaceCapabilitiesKHR(
|
|
||||||
ctx.pdevice.*,
|
|
||||||
ctx.surface.*,
|
|
||||||
);
|
|
||||||
self.cinfo.image_extent = capabilities.current_extent;
|
|
||||||
self.handle = try ctx.D.createSwapchainKHR(&self.cinfo, null);
|
|
||||||
ctx.D.destroySwapchainKHR(self.cinfo.old_swapchain, null);
|
|
||||||
errdefer ctx.D.destroySwapchainKHR(self.handle, null);
|
|
||||||
self.cinfo.old_swapchain = self.handle;
|
|
||||||
|
|
||||||
for (self.views.items) |view| ctx.D.destroyImageView(view, null);
|
|
||||||
|
|
||||||
var count: u32 = undefined;
|
|
||||||
std.debug.assert(.success == try ctx.D.getSwapchainImagesKHR(self.handle, &count, null));
|
|
||||||
try self.images.resize(self.alloc, count);
|
|
||||||
try self.views.resize(self.alloc, count);
|
|
||||||
std.debug.assert(.success == try ctx.D.getSwapchainImagesKHR(self.handle, &count, self.images.items.ptr));
|
|
||||||
|
|
||||||
@memset(self.views.items, .null_handle);
|
|
||||||
errdefer for (self.views.items) |view| ctx.D.destroyImageView(view, null);
|
|
||||||
|
|
||||||
for (self.images.items, self.views.items) |image, *view| {
|
|
||||||
view.* = try ctx.D.createImageView(&vk.ImageViewCreateInfo{
|
|
||||||
.image = image,
|
|
||||||
.view_type = .@"2d",
|
|
||||||
.format = self.cinfo.image_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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
Reference in New Issue
Block a user