nu simplified; working swapchain, queue, and validators.
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
This commit is contained in:
@@ -8,7 +8,7 @@
|
||||
.hash = "1220cf0972c6fe05437c1a8689b955084385eb7ca1f8c14010d49ca5a89570a5d90d",
|
||||
},
|
||||
.cimgui = .{
|
||||
.path="cimgui",
|
||||
.path = "cimgui",
|
||||
},
|
||||
},
|
||||
|
||||
|
110
src/main.zig
110
src/main.zig
@@ -3,7 +3,7 @@ const nu = @import("nu.zig");
|
||||
|
||||
pub const nu_modules = .{
|
||||
App,
|
||||
UI,
|
||||
// UI,
|
||||
};
|
||||
pub const main = nu.main;
|
||||
|
||||
@@ -15,62 +15,62 @@ pub const nu_options: nu.Options = .{
|
||||
},
|
||||
};
|
||||
|
||||
pub const UI = struct {
|
||||
const im = nu.ImGui;
|
||||
|
||||
pub const depends = .{im};
|
||||
|
||||
var color: @Vector(4, f32) = @splat(1);
|
||||
|
||||
pub fn setup(_: std.mem.Allocator) !void {
|
||||
const io: *nu.ImGui.ImGuiIO = @ptrCast(nu.ImGui.igGetIO());
|
||||
io.ConfigFlags |= nu.ImGui.ImGuiConfigFlags_DockingEnable;
|
||||
}
|
||||
|
||||
pub fn frame() !void {
|
||||
nu.ImGui.igShowMetricsWindow(null);
|
||||
|
||||
{
|
||||
const viewport = im.igGetMainViewport();
|
||||
im.igSetNextWindowPos(viewport.*.WorkPos, 0, .{ .x = 0, .y = 0 });
|
||||
im.igSetNextWindowSize(viewport.*.WorkSize, 0);
|
||||
im.igSetNextWindowViewport(viewport.*.ID);
|
||||
im.igPushStyleVar_Float(im.ImGuiStyleVar_WindowRounding, 0);
|
||||
im.igPushStyleVar_Float(im.ImGuiStyleVar_WindowBorderSize, 0);
|
||||
im.igPushStyleVar_Vec2(im.ImGuiStyleVar_WindowPadding, .{ .x = 0, .y = 0 });
|
||||
defer im.igPopStyleVar(3);
|
||||
|
||||
const window_flags =
|
||||
im.ImGuiWindowFlags_MenuBar |
|
||||
im.ImGuiWindowFlags_NoDocking |
|
||||
im.ImGuiWindowFlags_NoTitleBar |
|
||||
im.ImGuiWindowFlags_NoCollapse |
|
||||
im.ImGuiWindowFlags_NoResize |
|
||||
im.ImGuiWindowFlags_NoMove |
|
||||
im.ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
im.ImGuiWindowFlags_NoNavFocus |
|
||||
im.ImGuiWindowFlags_NoBackground;
|
||||
|
||||
const dock_flags =
|
||||
im.ImGuiDockNodeFlags_PassthruCentralNode |
|
||||
im.ImGuiDockNodeFlags_NoDockingOverCentralNode;
|
||||
|
||||
_ = im.igBegin("Main Dockspace", null, window_flags);
|
||||
const id = im.igGetID_Str("maindockspace");
|
||||
_ = im.igDockSpace(id, .{ .x = 0, .y = 0 }, dock_flags, null);
|
||||
im.igEnd();
|
||||
}
|
||||
|
||||
if (nu.ImGui.igBegin("Color", null, nu.ImGui.ImGuiWindowFlags_None)) {
|
||||
if (nu.ImGui.igColorEdit4("color", @ptrCast(&color), nu.ImGui.ImGuiColorEditFlags_AlphaPreviewHalf)) {}
|
||||
}
|
||||
nu.ImGui.igEnd();
|
||||
}
|
||||
};
|
||||
// pub const UI = struct {
|
||||
// const im = nu.ImGui;
|
||||
//
|
||||
// pub const depends = .{im};
|
||||
//
|
||||
// var color: @Vector(4, f32) = @splat(1);
|
||||
//
|
||||
// pub fn setup(_: std.mem.Allocator) !void {
|
||||
// const io: *nu.ImGui.ImGuiIO = @ptrCast(nu.ImGui.igGetIO());
|
||||
// io.ConfigFlags |= nu.ImGui.ImGuiConfigFlags_DockingEnable;
|
||||
// }
|
||||
//
|
||||
// pub fn frame() !void {
|
||||
// nu.ImGui.igShowMetricsWindow(null);
|
||||
//
|
||||
// {
|
||||
// const viewport = im.igGetMainViewport();
|
||||
// im.igSetNextWindowPos(viewport.*.WorkPos, 0, .{ .x = 0, .y = 0 });
|
||||
// im.igSetNextWindowSize(viewport.*.WorkSize, 0);
|
||||
// im.igSetNextWindowViewport(viewport.*.ID);
|
||||
// im.igPushStyleVar_Float(im.ImGuiStyleVar_WindowRounding, 0);
|
||||
// im.igPushStyleVar_Float(im.ImGuiStyleVar_WindowBorderSize, 0);
|
||||
// im.igPushStyleVar_Vec2(im.ImGuiStyleVar_WindowPadding, .{ .x = 0, .y = 0 });
|
||||
// defer im.igPopStyleVar(3);
|
||||
//
|
||||
// const window_flags =
|
||||
// im.ImGuiWindowFlags_MenuBar |
|
||||
// im.ImGuiWindowFlags_NoDocking |
|
||||
// im.ImGuiWindowFlags_NoTitleBar |
|
||||
// im.ImGuiWindowFlags_NoCollapse |
|
||||
// im.ImGuiWindowFlags_NoResize |
|
||||
// im.ImGuiWindowFlags_NoMove |
|
||||
// im.ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||
// im.ImGuiWindowFlags_NoNavFocus |
|
||||
// im.ImGuiWindowFlags_NoBackground;
|
||||
//
|
||||
// const dock_flags =
|
||||
// im.ImGuiDockNodeFlags_PassthruCentralNode |
|
||||
// im.ImGuiDockNodeFlags_NoDockingOverCentralNode;
|
||||
//
|
||||
// _ = im.igBegin("Main Dockspace", null, window_flags);
|
||||
// const id = im.igGetID_Str("maindockspace");
|
||||
// _ = im.igDockSpace(id, .{ .x = 0, .y = 0 }, dock_flags, null);
|
||||
// im.igEnd();
|
||||
// }
|
||||
//
|
||||
// if (nu.ImGui.igBegin("Color", null, nu.ImGui.ImGuiWindowFlags_None)) {
|
||||
// if (nu.ImGui.igColorEdit4("color", @ptrCast(&color), nu.ImGui.ImGuiColorEditFlags_AlphaPreviewHalf)) {}
|
||||
// }
|
||||
// nu.ImGui.igEnd();
|
||||
// }
|
||||
// };
|
||||
|
||||
const App = struct {
|
||||
const vk = @import("vk");
|
||||
const au = @import("nu/Render/au.zig");
|
||||
// const au = @import("nu/Render/au.zig");
|
||||
|
||||
pub const depends = .{nu.Render};
|
||||
|
||||
@@ -82,5 +82,5 @@ const App = struct {
|
||||
|
||||
pub fn frame() !void {}
|
||||
|
||||
pub fn present(_: au.CommandBufferProxy) void {}
|
||||
// pub fn present(_: au.CommandBufferProxy) void {}
|
||||
};
|
||||
|
@@ -3,7 +3,7 @@ const root = @import("root");
|
||||
|
||||
pub const Window = @import("nu/Window.zig");
|
||||
pub const Render = @import("nu/Render.zig");
|
||||
pub const ImGui = @import("nu/ImGui.zig");
|
||||
// pub const ImGui = @import("nu/ImGui.zig");
|
||||
|
||||
pub const Bus = @import("nu/Bus.zig");
|
||||
|
||||
@@ -109,5 +109,4 @@ const Graph = struct {
|
||||
}
|
||||
};
|
||||
|
||||
test {
|
||||
}
|
||||
test {}
|
||||
|
@@ -4,7 +4,6 @@ const std = @import("std");
|
||||
|
||||
const vk = @import("vk");
|
||||
const nu = @import("../nu.zig");
|
||||
const au = @import("Render/au.zig");
|
||||
|
||||
const Render = @import("Render.zig");
|
||||
const Window = @import("Window.zig");
|
||||
@@ -18,7 +17,7 @@ const config = nu.config.imgui;
|
||||
pub const depends = .{ Render, Window };
|
||||
|
||||
pub fn loader_wrapper(procname: [*c]const u8, _: ?*anyopaque) callconv(.C) vk.PfnVoidFunction {
|
||||
return au.glfwGetInstanceProcAddress(au.I.handle, procname);
|
||||
return nu.glfwGetInstanceProcAddress(nu.I.handle, procname);
|
||||
}
|
||||
|
||||
var ctx: *im.ImGuiContext = undefined;
|
||||
@@ -39,23 +38,27 @@ pub fn setup(_: std.mem.Allocator) !void {
|
||||
}
|
||||
errdefer im.impl.ImGui_ImplGlfw_Shutdown();
|
||||
|
||||
descriptor_pool = try au.D.createDescriptorPool(&vk.DescriptorPoolCreateInfo{
|
||||
.flags = .{ .free_descriptor_set_bit = true },
|
||||
.pool_size_count = 1,
|
||||
.p_pool_sizes = &.{vk.DescriptorPoolSize{
|
||||
.descriptor_count = 32,
|
||||
.type = .combined_image_sampler,
|
||||
}},
|
||||
.max_sets = 32,
|
||||
}, null);
|
||||
errdefer au.D.destroyDescriptorPool(descriptor_pool, null);
|
||||
descriptor_pool = try Render.ctx.dw.createDescriptorPool(
|
||||
Render.ctx.device,
|
||||
&vk.DescriptorPoolCreateInfo{
|
||||
.flags = .{ .free_descriptor_set_bit = true },
|
||||
.pool_size_count = 1,
|
||||
.p_pool_sizes = &.{vk.DescriptorPoolSize{
|
||||
.descriptor_count = 32,
|
||||
.type = .combined_image_sampler,
|
||||
}},
|
||||
.max_sets = 32,
|
||||
},
|
||||
null,
|
||||
);
|
||||
errdefer Render.ctx.dw.destroyDescriptorPool(Render.ctx.device, descriptor_pool, null);
|
||||
|
||||
if (im.impl.ImGui_ImplVulkan_Init(@constCast(&im.impl.ImGui_ImplVulkan_InitInfo{
|
||||
.Instance = @ptrFromInt(@intFromEnum(au.I.handle)),
|
||||
.PhysicalDevice = @ptrFromInt(@intFromEnum(au.device_config.pdev)),
|
||||
.Device = @ptrFromInt(@intFromEnum(au.D.handle)),
|
||||
.QueueFamily = au.device_config.family,
|
||||
.Queue = @ptrFromInt(@intFromEnum(au.Q.handle)),
|
||||
.Instance = @ptrFromInt(@intFromEnum(Render.ctx.instance)),
|
||||
.PhysicalDevice = @ptrFromInt(@intFromEnum(Render.ctx.pdevice)),
|
||||
.Device = @ptrFromInt(@intFromEnum(Render.ctx.device)),
|
||||
.QueueFamily = au.device_config.family, // todo
|
||||
.Queue = @ptrFromInt(@intFromEnum(au.Q.handle)), // todo
|
||||
.DescriptorPool = @ptrFromInt(@intFromEnum(descriptor_pool)),
|
||||
.RenderPass = null,
|
||||
.MinImageCount = 2,
|
||||
@@ -65,7 +68,7 @@ pub fn setup(_: std.mem.Allocator) !void {
|
||||
.depth_attachment_format = .undefined,
|
||||
.stencil_attachment_format = .undefined,
|
||||
.color_attachment_count = 1,
|
||||
.p_color_attachment_formats = &.{au.device_config.format.format},
|
||||
.p_color_attachment_formats = &.{au.device_config.format.format}, // todo
|
||||
}),
|
||||
.MSAASamples = 0,
|
||||
.PipelineCache = null,
|
||||
@@ -83,9 +86,9 @@ pub fn setup(_: std.mem.Allocator) !void {
|
||||
}
|
||||
|
||||
pub fn teardown() void {
|
||||
au.D.deviceWaitIdle() catch |err| std.debug.panic("Device wait failed: {!}", .{err});
|
||||
Render.ctx.dw.deviceWaitIdle(Render.ctx.device) catch |err| std.debug.panic("Device wait failed: {!}", .{err});
|
||||
im.impl.ImGui_ImplVulkan_Shutdown();
|
||||
au.D.destroyDescriptorPool(descriptor_pool, null);
|
||||
Render.ctx.dw.destroyDescriptorPool(Render.ctx.device, descriptor_pool, null);
|
||||
im.impl.ImGui_ImplGlfw_Shutdown();
|
||||
im.igDestroyContext(ctx);
|
||||
}
|
||||
@@ -96,7 +99,7 @@ pub fn frame() !void {
|
||||
im.igNewFrame();
|
||||
}
|
||||
|
||||
pub fn present(cmd: au.CommandBufferProxy) void {
|
||||
pub fn present(cmd: au.CommandBufferProxy) void { // todo
|
||||
im.igEndFrame();
|
||||
im.igRender();
|
||||
|
||||
|
@@ -7,7 +7,10 @@ const builtin = @import("builtin");
|
||||
const vk = @import("vk");
|
||||
|
||||
const nu = @import("../nu.zig");
|
||||
const au = @import("Render/au.zig");
|
||||
// const au = @import("Render/au.zig");
|
||||
|
||||
const ctx = @import("Render/ctx.zig");
|
||||
const swap_chain = @import("Render/swap_chain.zig");
|
||||
|
||||
pub const Config = struct {
|
||||
app_name: [*:0]const u8 = "nu-au-app",
|
||||
@@ -24,7 +27,6 @@ pub const Config = struct {
|
||||
minor: u10 = 0,
|
||||
patch: u12 = 0,
|
||||
} = .{},
|
||||
frames_in_flight: u8 = 3,
|
||||
use_debug_messenger: bool = switch (builtin.mode) {
|
||||
.Debug, .ReleaseSafe => true,
|
||||
.ReleaseSmall, .ReleaseFast => false,
|
||||
@@ -34,84 +36,178 @@ const config = nu.config.render;
|
||||
|
||||
pub const depends = .{nu.Window};
|
||||
|
||||
var sc: au.SwapChain = undefined;
|
||||
var flights: au.Flights = undefined;
|
||||
const SwapChain = swap_chain.SwapChain(Flight);
|
||||
const Flight = struct {
|
||||
pool: vk.CommandPool = .null_handle,
|
||||
cmd: vk.CommandBuffer = .null_handle,
|
||||
|
||||
pub fn init() !Flight {
|
||||
const pool = try ctx.D.createCommandPool(
|
||||
&.{ .queue_family_index = ctx.family.* },
|
||||
null,
|
||||
);
|
||||
errdefer ctx.D.destroyCommandPool(pool, null);
|
||||
|
||||
var cmds: [1]vk.CommandBuffer = undefined;
|
||||
try ctx.D.allocateCommandBuffers(
|
||||
&vk.CommandBufferAllocateInfo{
|
||||
.command_buffer_count = 1,
|
||||
.command_pool = pool,
|
||||
.level = .primary,
|
||||
},
|
||||
&cmds,
|
||||
);
|
||||
errdefer ctx.D.freeCommandBuffers(pool, 1, &cmds);
|
||||
|
||||
return .{
|
||||
.pool = pool,
|
||||
.cmd = cmds[0],
|
||||
};
|
||||
}
|
||||
|
||||
pub fn deinit(self: Flight) void {
|
||||
const cmds: [1]vk.CommandBuffer = .{self.cmd};
|
||||
ctx.D.freeCommandBuffers(self.pool, 1, &cmds);
|
||||
ctx.D.destroyCommandPool(self.pool, null);
|
||||
}
|
||||
};
|
||||
|
||||
var _sc: SwapChain = undefined;
|
||||
var _flights: []Flight = undefined;
|
||||
|
||||
pub fn setup(alloc: std.mem.Allocator) !void {
|
||||
// todo pick apart au into helpers; not a sub-module filled with its own globals.
|
||||
try au.init(alloc);
|
||||
errdefer au.deinit();
|
||||
try ctx.init(alloc);
|
||||
errdefer ctx.deinit();
|
||||
|
||||
sc = try au.SwapChain.init(alloc);
|
||||
errdefer sc.deinit();
|
||||
_flights = try alloc.alloc(Flight, 3);
|
||||
errdefer alloc.free(_flights);
|
||||
errdefer for (_flights) |flight| flight.deinit();
|
||||
for (_flights) |*flight| flight.* = try Flight.init();
|
||||
|
||||
flights = try au.Flights.init(alloc, config.frames_in_flight);
|
||||
errdefer flights.deinit();
|
||||
_sc = try SwapChain.init(alloc, _flights);
|
||||
errdefer _sc.deinit();
|
||||
}
|
||||
|
||||
pub fn teardown() void {
|
||||
au.D.deviceWaitIdle() catch |err| std.debug.panic("Device wait failed: {!}", .{err});
|
||||
flights.deinit();
|
||||
sc.deinit();
|
||||
au.deinit();
|
||||
_sc.deinit();
|
||||
for (_flights) |flight| flight.deinit();
|
||||
ctx.deinit();
|
||||
}
|
||||
|
||||
pub fn render() !void {
|
||||
const flight: au.Flights.Flight = flights.next();
|
||||
try flight.wait();
|
||||
const target = try _sc.acquire();
|
||||
|
||||
while (true) {
|
||||
_ = try sc.rebuild();
|
||||
const render_area: vk.Rect2D = .{
|
||||
.offset = .{ .x = 0, .y = 0 },
|
||||
.extent = _sc.cinfo.image_extent,
|
||||
};
|
||||
|
||||
const target = sc.acquire(flight.acquire, .null_handle) catch |err| switch (err) {
|
||||
error.OutOfDateKHR => {
|
||||
sc.mark();
|
||||
continue;
|
||||
try ctx.D.resetCommandPool(target.flight.pool, .{});
|
||||
var cmd = ctx.CommandBufferProxy.init(target.flight.cmd, ctx.dw);
|
||||
|
||||
try cmd.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
||||
{
|
||||
cmd.pipelineBarrier(
|
||||
.{ .top_of_pipe_bit = true },
|
||||
.{ .color_attachment_output_bit = true },
|
||||
.{},
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
&.{
|
||||
vk.ImageMemoryBarrier{
|
||||
.src_access_mask = .{},
|
||||
.dst_access_mask = .{ .color_attachment_write_bit = true },
|
||||
.old_layout = .undefined,
|
||||
.new_layout = .color_attachment_optimal,
|
||||
.src_queue_family_index = 0, // values are the same; no transfer occurs
|
||||
.dst_queue_family_index = 0,
|
||||
.image = target.image,
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
else => return err,
|
||||
};
|
||||
);
|
||||
|
||||
const render_area: vk.Rect2D = .{
|
||||
.offset = .{ .x = 0, .y = 0 },
|
||||
.extent = sc.cinfo.image_extent,
|
||||
};
|
||||
cmd.beginRendering(&vk.RenderingInfo{
|
||||
.render_area = render_area,
|
||||
.layer_count = 1,
|
||||
.view_mask = 0,
|
||||
.color_attachment_count = 1,
|
||||
.p_color_attachments = &.{
|
||||
vk.RenderingAttachmentInfo{
|
||||
.image_view = target.view,
|
||||
.image_layout = .color_attachment_optimal,
|
||||
.resolve_mode = .{},
|
||||
.resolve_image_view = .null_handle,
|
||||
.resolve_image_layout = .undefined,
|
||||
.load_op = .clear,
|
||||
.store_op = .store,
|
||||
.clear_value = .{ .color = .{ .float_32 = .{ 1, 0, 0, 1 } } },
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
try au.D.resetCommandPool(flight.pool, .{});
|
||||
var cmd = au.CommandBufferProxy.init(flight.cmd, au.D.wrapper);
|
||||
cmd.endRendering();
|
||||
|
||||
try cmd.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
||||
target.begin_rendering(cmd, render_area);
|
||||
cmd.pipelineBarrier(
|
||||
.{ .color_attachment_output_bit = true },
|
||||
.{ .bottom_of_pipe_bit = true },
|
||||
.{},
|
||||
0,
|
||||
null,
|
||||
0,
|
||||
null,
|
||||
1,
|
||||
&.{
|
||||
vk.ImageMemoryBarrier{
|
||||
.src_access_mask = .{ .color_attachment_write_bit = true },
|
||||
.dst_access_mask = .{},
|
||||
.old_layout = .color_attachment_optimal,
|
||||
.new_layout = .present_src_khr,
|
||||
.src_queue_family_index = 0, // values are the same; no transfer occurs.
|
||||
.dst_queue_family_index = 0,
|
||||
.image = target.image,
|
||||
.subresource_range = .{
|
||||
.aspect_mask = .{ .color_bit = true },
|
||||
.base_mip_level = 0,
|
||||
.level_count = 1,
|
||||
.base_array_layer = 0,
|
||||
.layer_count = 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
try cmd.endCommandBuffer();
|
||||
|
||||
// todo manage frame in flight state for each hook; pass the current flight in as context.
|
||||
|
||||
nu.engine.invoke("present", .{cmd});
|
||||
|
||||
target.end_rendering(cmd);
|
||||
try cmd.endCommandBuffer();
|
||||
|
||||
try au.Q.submit(1, &.{
|
||||
try ctx.Q.submit(
|
||||
1,
|
||||
&.{
|
||||
vk.SubmitInfo{
|
||||
.wait_semaphore_count = 1,
|
||||
.p_wait_semaphores = &.{flight.acquire},
|
||||
// don't start writing to color attachment until the swapchain image has been acquired.
|
||||
.p_wait_semaphores = &.{
|
||||
target.acquired,
|
||||
},
|
||||
.p_wait_dst_stage_mask = &.{
|
||||
vk.PipelineStageFlags{ .color_attachment_output_bit = true },
|
||||
},
|
||||
.command_buffer_count = 1,
|
||||
.p_command_buffers = &.{cmd.handle},
|
||||
.p_command_buffers = &.{target.flight.cmd},
|
||||
.signal_semaphore_count = 1,
|
||||
.p_signal_semaphores = &.{flight.complete},
|
||||
.p_signal_semaphores = &.{target.complete},
|
||||
},
|
||||
}, flight.fence);
|
||||
},
|
||||
target.available, // target will become available again once these finish
|
||||
);
|
||||
|
||||
if (sc.present(&.{flight.complete}, target)) |_| {
|
||||
return;
|
||||
} else |err| switch (err) {
|
||||
error.OutOfDateKHR => {
|
||||
try flight.wait();
|
||||
sc.mark();
|
||||
continue;
|
||||
},
|
||||
else => return err,
|
||||
}
|
||||
}
|
||||
try _sc.present(target);
|
||||
}
|
||||
|
66
src/nu/Render/Debug.zig
Normal file
66
src/nu/Render/Debug.zig
Normal file
@@ -0,0 +1,66 @@
|
||||
const std = @import("std");
|
||||
const vk = @import("vk");
|
||||
|
||||
pub const ci: 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 = true,
|
||||
.performance_bit_ext = true,
|
||||
.validation_bit_ext = true,
|
||||
},
|
||||
.pfn_user_callback = &debug_callback,
|
||||
.p_user_data = 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;
|
||||
}
|
@@ -99,7 +99,12 @@ pub fn rebuild(self: *Self) !bool {
|
||||
}
|
||||
|
||||
pub fn acquire(self: Self, semaphore: vk.Semaphore, fence: vk.Fence) !Target {
|
||||
const acq = try au.D.acquireNextImageKHR(self.handle, std.math.maxInt(u64), semaphore, fence);
|
||||
const acq = try au.D.acquireNextImageKHR(
|
||||
self.handle,
|
||||
std.math.maxInt(u64),
|
||||
semaphore,
|
||||
fence,
|
||||
);
|
||||
return .{
|
||||
.idx = acq.image_index,
|
||||
.image = self.images.items[acq.image_index],
|
||||
|
298
src/nu/Render/ctx.zig
Normal file
298
src/nu/Render/ctx.zig
Normal file
@@ -0,0 +1,298 @@
|
||||
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;
|
283
src/nu/Render/swap_chain.zig
Normal file
283
src/nu/Render/swap_chain.zig
Normal file
@@ -0,0 +1,283 @@
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
@@ -40,6 +40,9 @@ pub var handle: *c.GLFWwindow = undefined;
|
||||
pub fn setup(_: std.mem.Allocator) !void {
|
||||
if (c.glfwInit() != c.GLFW_TRUE) std.debug.panic("GLFW Init Failed", .{});
|
||||
|
||||
if (c.glfwVulkanSupported() != c.GLFW_TRUE)
|
||||
std.debug.panic("GLFW Vulkan not supported", .{});
|
||||
|
||||
c.glfwWindowHint(c.GLFW_CLIENT_API, c.GLFW_NO_API);
|
||||
c.glfwWindowHintString(c.GLFW_X11_CLASS_NAME, config.x11_class_name);
|
||||
c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, config.x11_instance_name);
|
||||
|
Reference in New Issue
Block a user