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",
|
.hash = "1220cf0972c6fe05437c1a8689b955084385eb7ca1f8c14010d49ca5a89570a5d90d",
|
||||||
},
|
},
|
||||||
.cimgui = .{
|
.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 = .{
|
pub const nu_modules = .{
|
||||||
App,
|
App,
|
||||||
UI,
|
// UI,
|
||||||
};
|
};
|
||||||
pub const main = nu.main;
|
pub const main = nu.main;
|
||||||
|
|
||||||
@@ -15,62 +15,62 @@ pub const nu_options: nu.Options = .{
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const UI = struct {
|
// pub const UI = struct {
|
||||||
const im = nu.ImGui;
|
// const im = nu.ImGui;
|
||||||
|
//
|
||||||
pub const depends = .{im};
|
// pub const depends = .{im};
|
||||||
|
//
|
||||||
var color: @Vector(4, f32) = @splat(1);
|
// var color: @Vector(4, f32) = @splat(1);
|
||||||
|
//
|
||||||
pub fn setup(_: std.mem.Allocator) !void {
|
// pub fn setup(_: std.mem.Allocator) !void {
|
||||||
const io: *nu.ImGui.ImGuiIO = @ptrCast(nu.ImGui.igGetIO());
|
// const io: *nu.ImGui.ImGuiIO = @ptrCast(nu.ImGui.igGetIO());
|
||||||
io.ConfigFlags |= nu.ImGui.ImGuiConfigFlags_DockingEnable;
|
// io.ConfigFlags |= nu.ImGui.ImGuiConfigFlags_DockingEnable;
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
pub fn frame() !void {
|
// pub fn frame() !void {
|
||||||
nu.ImGui.igShowMetricsWindow(null);
|
// nu.ImGui.igShowMetricsWindow(null);
|
||||||
|
//
|
||||||
{
|
// {
|
||||||
const viewport = im.igGetMainViewport();
|
// const viewport = im.igGetMainViewport();
|
||||||
im.igSetNextWindowPos(viewport.*.WorkPos, 0, .{ .x = 0, .y = 0 });
|
// im.igSetNextWindowPos(viewport.*.WorkPos, 0, .{ .x = 0, .y = 0 });
|
||||||
im.igSetNextWindowSize(viewport.*.WorkSize, 0);
|
// im.igSetNextWindowSize(viewport.*.WorkSize, 0);
|
||||||
im.igSetNextWindowViewport(viewport.*.ID);
|
// im.igSetNextWindowViewport(viewport.*.ID);
|
||||||
im.igPushStyleVar_Float(im.ImGuiStyleVar_WindowRounding, 0);
|
// im.igPushStyleVar_Float(im.ImGuiStyleVar_WindowRounding, 0);
|
||||||
im.igPushStyleVar_Float(im.ImGuiStyleVar_WindowBorderSize, 0);
|
// im.igPushStyleVar_Float(im.ImGuiStyleVar_WindowBorderSize, 0);
|
||||||
im.igPushStyleVar_Vec2(im.ImGuiStyleVar_WindowPadding, .{ .x = 0, .y = 0 });
|
// im.igPushStyleVar_Vec2(im.ImGuiStyleVar_WindowPadding, .{ .x = 0, .y = 0 });
|
||||||
defer im.igPopStyleVar(3);
|
// defer im.igPopStyleVar(3);
|
||||||
|
//
|
||||||
const window_flags =
|
// const window_flags =
|
||||||
im.ImGuiWindowFlags_MenuBar |
|
// im.ImGuiWindowFlags_MenuBar |
|
||||||
im.ImGuiWindowFlags_NoDocking |
|
// im.ImGuiWindowFlags_NoDocking |
|
||||||
im.ImGuiWindowFlags_NoTitleBar |
|
// im.ImGuiWindowFlags_NoTitleBar |
|
||||||
im.ImGuiWindowFlags_NoCollapse |
|
// im.ImGuiWindowFlags_NoCollapse |
|
||||||
im.ImGuiWindowFlags_NoResize |
|
// im.ImGuiWindowFlags_NoResize |
|
||||||
im.ImGuiWindowFlags_NoMove |
|
// im.ImGuiWindowFlags_NoMove |
|
||||||
im.ImGuiWindowFlags_NoBringToFrontOnFocus |
|
// im.ImGuiWindowFlags_NoBringToFrontOnFocus |
|
||||||
im.ImGuiWindowFlags_NoNavFocus |
|
// im.ImGuiWindowFlags_NoNavFocus |
|
||||||
im.ImGuiWindowFlags_NoBackground;
|
// im.ImGuiWindowFlags_NoBackground;
|
||||||
|
//
|
||||||
const dock_flags =
|
// const dock_flags =
|
||||||
im.ImGuiDockNodeFlags_PassthruCentralNode |
|
// im.ImGuiDockNodeFlags_PassthruCentralNode |
|
||||||
im.ImGuiDockNodeFlags_NoDockingOverCentralNode;
|
// im.ImGuiDockNodeFlags_NoDockingOverCentralNode;
|
||||||
|
//
|
||||||
_ = im.igBegin("Main Dockspace", null, window_flags);
|
// _ = im.igBegin("Main Dockspace", null, window_flags);
|
||||||
const id = im.igGetID_Str("maindockspace");
|
// const id = im.igGetID_Str("maindockspace");
|
||||||
_ = im.igDockSpace(id, .{ .x = 0, .y = 0 }, dock_flags, null);
|
// _ = im.igDockSpace(id, .{ .x = 0, .y = 0 }, dock_flags, null);
|
||||||
im.igEnd();
|
// im.igEnd();
|
||||||
}
|
// }
|
||||||
|
//
|
||||||
if (nu.ImGui.igBegin("Color", null, nu.ImGui.ImGuiWindowFlags_None)) {
|
// if (nu.ImGui.igBegin("Color", null, nu.ImGui.ImGuiWindowFlags_None)) {
|
||||||
if (nu.ImGui.igColorEdit4("color", @ptrCast(&color), nu.ImGui.ImGuiColorEditFlags_AlphaPreviewHalf)) {}
|
// if (nu.ImGui.igColorEdit4("color", @ptrCast(&color), nu.ImGui.ImGuiColorEditFlags_AlphaPreviewHalf)) {}
|
||||||
}
|
// }
|
||||||
nu.ImGui.igEnd();
|
// nu.ImGui.igEnd();
|
||||||
}
|
// }
|
||||||
};
|
// };
|
||||||
|
|
||||||
const App = struct {
|
const App = struct {
|
||||||
const vk = @import("vk");
|
const vk = @import("vk");
|
||||||
const au = @import("nu/Render/au.zig");
|
// const au = @import("nu/Render/au.zig");
|
||||||
|
|
||||||
pub const depends = .{nu.Render};
|
pub const depends = .{nu.Render};
|
||||||
|
|
||||||
@@ -82,5 +82,5 @@ const App = struct {
|
|||||||
|
|
||||||
pub fn frame() !void {}
|
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 Window = @import("nu/Window.zig");
|
||||||
pub const Render = @import("nu/Render.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");
|
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 vk = @import("vk");
|
||||||
const nu = @import("../nu.zig");
|
const nu = @import("../nu.zig");
|
||||||
const au = @import("Render/au.zig");
|
|
||||||
|
|
||||||
const Render = @import("Render.zig");
|
const Render = @import("Render.zig");
|
||||||
const Window = @import("Window.zig");
|
const Window = @import("Window.zig");
|
||||||
@@ -18,7 +17,7 @@ const config = nu.config.imgui;
|
|||||||
pub const depends = .{ Render, Window };
|
pub const depends = .{ Render, Window };
|
||||||
|
|
||||||
pub fn loader_wrapper(procname: [*c]const u8, _: ?*anyopaque) callconv(.C) vk.PfnVoidFunction {
|
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;
|
var ctx: *im.ImGuiContext = undefined;
|
||||||
@@ -39,23 +38,27 @@ pub fn setup(_: std.mem.Allocator) !void {
|
|||||||
}
|
}
|
||||||
errdefer im.impl.ImGui_ImplGlfw_Shutdown();
|
errdefer im.impl.ImGui_ImplGlfw_Shutdown();
|
||||||
|
|
||||||
descriptor_pool = try au.D.createDescriptorPool(&vk.DescriptorPoolCreateInfo{
|
descriptor_pool = try Render.ctx.dw.createDescriptorPool(
|
||||||
.flags = .{ .free_descriptor_set_bit = true },
|
Render.ctx.device,
|
||||||
.pool_size_count = 1,
|
&vk.DescriptorPoolCreateInfo{
|
||||||
.p_pool_sizes = &.{vk.DescriptorPoolSize{
|
.flags = .{ .free_descriptor_set_bit = true },
|
||||||
.descriptor_count = 32,
|
.pool_size_count = 1,
|
||||||
.type = .combined_image_sampler,
|
.p_pool_sizes = &.{vk.DescriptorPoolSize{
|
||||||
}},
|
.descriptor_count = 32,
|
||||||
.max_sets = 32,
|
.type = .combined_image_sampler,
|
||||||
}, null);
|
}},
|
||||||
errdefer au.D.destroyDescriptorPool(descriptor_pool, null);
|
.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{
|
if (im.impl.ImGui_ImplVulkan_Init(@constCast(&im.impl.ImGui_ImplVulkan_InitInfo{
|
||||||
.Instance = @ptrFromInt(@intFromEnum(au.I.handle)),
|
.Instance = @ptrFromInt(@intFromEnum(Render.ctx.instance)),
|
||||||
.PhysicalDevice = @ptrFromInt(@intFromEnum(au.device_config.pdev)),
|
.PhysicalDevice = @ptrFromInt(@intFromEnum(Render.ctx.pdevice)),
|
||||||
.Device = @ptrFromInt(@intFromEnum(au.D.handle)),
|
.Device = @ptrFromInt(@intFromEnum(Render.ctx.device)),
|
||||||
.QueueFamily = au.device_config.family,
|
.QueueFamily = au.device_config.family, // todo
|
||||||
.Queue = @ptrFromInt(@intFromEnum(au.Q.handle)),
|
.Queue = @ptrFromInt(@intFromEnum(au.Q.handle)), // todo
|
||||||
.DescriptorPool = @ptrFromInt(@intFromEnum(descriptor_pool)),
|
.DescriptorPool = @ptrFromInt(@intFromEnum(descriptor_pool)),
|
||||||
.RenderPass = null,
|
.RenderPass = null,
|
||||||
.MinImageCount = 2,
|
.MinImageCount = 2,
|
||||||
@@ -65,7 +68,7 @@ pub fn setup(_: std.mem.Allocator) !void {
|
|||||||
.depth_attachment_format = .undefined,
|
.depth_attachment_format = .undefined,
|
||||||
.stencil_attachment_format = .undefined,
|
.stencil_attachment_format = .undefined,
|
||||||
.color_attachment_count = 1,
|
.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,
|
.MSAASamples = 0,
|
||||||
.PipelineCache = null,
|
.PipelineCache = null,
|
||||||
@@ -83,9 +86,9 @@ pub fn setup(_: std.mem.Allocator) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn teardown() 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();
|
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.impl.ImGui_ImplGlfw_Shutdown();
|
||||||
im.igDestroyContext(ctx);
|
im.igDestroyContext(ctx);
|
||||||
}
|
}
|
||||||
@@ -96,7 +99,7 @@ pub fn frame() !void {
|
|||||||
im.igNewFrame();
|
im.igNewFrame();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn present(cmd: au.CommandBufferProxy) void {
|
pub fn present(cmd: au.CommandBufferProxy) void { // todo
|
||||||
im.igEndFrame();
|
im.igEndFrame();
|
||||||
im.igRender();
|
im.igRender();
|
||||||
|
|
||||||
|
@@ -7,7 +7,10 @@ const builtin = @import("builtin");
|
|||||||
const vk = @import("vk");
|
const vk = @import("vk");
|
||||||
|
|
||||||
const nu = @import("../nu.zig");
|
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 {
|
pub const Config = struct {
|
||||||
app_name: [*:0]const u8 = "nu-au-app",
|
app_name: [*:0]const u8 = "nu-au-app",
|
||||||
@@ -24,7 +27,6 @@ pub const Config = struct {
|
|||||||
minor: u10 = 0,
|
minor: u10 = 0,
|
||||||
patch: u12 = 0,
|
patch: u12 = 0,
|
||||||
} = .{},
|
} = .{},
|
||||||
frames_in_flight: u8 = 3,
|
|
||||||
use_debug_messenger: bool = switch (builtin.mode) {
|
use_debug_messenger: bool = switch (builtin.mode) {
|
||||||
.Debug, .ReleaseSafe => true,
|
.Debug, .ReleaseSafe => true,
|
||||||
.ReleaseSmall, .ReleaseFast => false,
|
.ReleaseSmall, .ReleaseFast => false,
|
||||||
@@ -34,84 +36,178 @@ const config = nu.config.render;
|
|||||||
|
|
||||||
pub const depends = .{nu.Window};
|
pub const depends = .{nu.Window};
|
||||||
|
|
||||||
var sc: au.SwapChain = undefined;
|
const SwapChain = swap_chain.SwapChain(Flight);
|
||||||
var flights: au.Flights = undefined;
|
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 {
|
pub fn setup(alloc: std.mem.Allocator) !void {
|
||||||
// todo pick apart au into helpers; not a sub-module filled with its own globals.
|
try ctx.init(alloc);
|
||||||
try au.init(alloc);
|
errdefer ctx.deinit();
|
||||||
errdefer au.deinit();
|
|
||||||
|
|
||||||
sc = try au.SwapChain.init(alloc);
|
_flights = try alloc.alloc(Flight, 3);
|
||||||
errdefer sc.deinit();
|
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);
|
_sc = try SwapChain.init(alloc, _flights);
|
||||||
errdefer flights.deinit();
|
errdefer _sc.deinit();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn teardown() void {
|
pub fn teardown() void {
|
||||||
au.D.deviceWaitIdle() catch |err| std.debug.panic("Device wait failed: {!}", .{err});
|
_sc.deinit();
|
||||||
flights.deinit();
|
for (_flights) |flight| flight.deinit();
|
||||||
sc.deinit();
|
ctx.deinit();
|
||||||
au.deinit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render() !void {
|
pub fn render() !void {
|
||||||
const flight: au.Flights.Flight = flights.next();
|
const target = try _sc.acquire();
|
||||||
try flight.wait();
|
|
||||||
|
|
||||||
while (true) {
|
const render_area: vk.Rect2D = .{
|
||||||
_ = try sc.rebuild();
|
.offset = .{ .x = 0, .y = 0 },
|
||||||
|
.extent = _sc.cinfo.image_extent,
|
||||||
|
};
|
||||||
|
|
||||||
const target = sc.acquire(flight.acquire, .null_handle) catch |err| switch (err) {
|
try ctx.D.resetCommandPool(target.flight.pool, .{});
|
||||||
error.OutOfDateKHR => {
|
var cmd = ctx.CommandBufferProxy.init(target.flight.cmd, ctx.dw);
|
||||||
sc.mark();
|
|
||||||
continue;
|
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 = .{
|
cmd.beginRendering(&vk.RenderingInfo{
|
||||||
.offset = .{ .x = 0, .y = 0 },
|
.render_area = render_area,
|
||||||
.extent = sc.cinfo.image_extent,
|
.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, .{});
|
cmd.endRendering();
|
||||||
var cmd = au.CommandBufferProxy.init(flight.cmd, au.D.wrapper);
|
|
||||||
|
|
||||||
try cmd.beginCommandBuffer(&.{ .flags = .{ .one_time_submit_bit = true } });
|
cmd.pipelineBarrier(
|
||||||
target.begin_rendering(cmd, render_area);
|
.{ .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.
|
try ctx.Q.submit(
|
||||||
|
1,
|
||||||
nu.engine.invoke("present", .{cmd});
|
&.{
|
||||||
|
|
||||||
target.end_rendering(cmd);
|
|
||||||
try cmd.endCommandBuffer();
|
|
||||||
|
|
||||||
try au.Q.submit(1, &.{
|
|
||||||
vk.SubmitInfo{
|
vk.SubmitInfo{
|
||||||
.wait_semaphore_count = 1,
|
.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 = &.{
|
.p_wait_dst_stage_mask = &.{
|
||||||
vk.PipelineStageFlags{ .color_attachment_output_bit = true },
|
vk.PipelineStageFlags{ .color_attachment_output_bit = true },
|
||||||
},
|
},
|
||||||
.command_buffer_count = 1,
|
.command_buffer_count = 1,
|
||||||
.p_command_buffers = &.{cmd.handle},
|
.p_command_buffers = &.{target.flight.cmd},
|
||||||
.signal_semaphore_count = 1,
|
.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)) |_| {
|
try _sc.present(target);
|
||||||
return;
|
|
||||||
} else |err| switch (err) {
|
|
||||||
error.OutOfDateKHR => {
|
|
||||||
try flight.wait();
|
|
||||||
sc.mark();
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
else => return err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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 {
|
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 .{
|
return .{
|
||||||
.idx = acq.image_index,
|
.idx = acq.image_index,
|
||||||
.image = self.images.items[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 {
|
pub fn setup(_: std.mem.Allocator) !void {
|
||||||
if (c.glfwInit() != c.GLFW_TRUE) std.debug.panic("GLFW Init Failed", .{});
|
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.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_CLASS_NAME, config.x11_class_name);
|
||||||
c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, config.x11_instance_name);
|
c.glfwWindowHintString(c.GLFW_X11_INSTANCE_NAME, config.x11_instance_name);
|
||||||
|
Reference in New Issue
Block a user