diff --git a/build.zig b/build.zig index ee624d3..c23f4de 100644 --- a/build.zig +++ b/build.zig @@ -4,7 +4,7 @@ const vkgen = @import("vulkan-zig"); pub fn build(b: *std.Build) void { const target = b.standardTargetOptions(.{}); const optimize = b.standardOptimizeOption(.{ - .preferred_optimize_mode = .ReleaseSafe, + // .preferred_optimize_mode = .ReleaseSafe, }); const vk = b.dependency("vulkan-zig", .{ diff --git a/src/nu.zig b/src/nu.zig index aaacbb2..88c0a4b 100644 --- a/src/nu.zig +++ b/src/nu.zig @@ -18,6 +18,8 @@ pub const engine = Engine(Window, Render, root.nu_modules); // Hooks: setup, teardown, fixed, frame, present pub fn main() void { + std.log.info("use_debug_messenger: {}", .{config.render.use_debug_messenger}); + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; const alloc = gpa.allocator(); diff --git a/src/nu/Render.zig b/src/nu/Render.zig index b74c49a..5960530 100644 --- a/src/nu/Render.zig +++ b/src/nu/Render.zig @@ -7,7 +7,6 @@ const builtin = @import("builtin"); const vk = @import("vk"); const nu = @import("../nu.zig"); -// const au = @import("Render/au.zig"); const ctx = @import("Render/ctx.zig"); const SwapChain = @import("Render/SwapChain.zig"); @@ -85,6 +84,12 @@ pub fn setup(alloc: std.mem.Allocator) !void { _sc = try SwapChain.init(alloc, _flights.len); errdefer _sc.deinit(); + + nu.Window.add_resize_callback(&on_resize); +} + +fn on_resize(_: u32, _: u32) void { + _sc.rebuild() catch @panic("rebuild on resize failed"); } pub fn teardown() void { @@ -94,7 +99,7 @@ pub fn teardown() void { } pub fn render() !void { - const target = try _sc.acquire(); + const target = try _sc.acquire() orelse return; const flight = &_flights[target.flight_index]; const render_area: vk.Rect2D = .{ @@ -116,7 +121,7 @@ pub fn render() !void { 0, null, 1, - &.{target.top_of_pipe()}, + &.{target.top_of_pipe_barrier()}, ); cmd.beginRendering(&vk.RenderingInfo{ @@ -133,7 +138,7 @@ pub fn render() !void { .resolve_image_layout = .undefined, .load_op = .clear, .store_op = .store, - .clear_value = .{ .color = .{ .float_32 = .{ 1, 0, 0, 1 } } }, + .clear_value = .{ .color = .{ .float_32 = .{ 0.1, 0.1, 0.1, 1 } } }, }, }, }); @@ -149,7 +154,7 @@ pub fn render() !void { 0, null, 1, - &.{target.bottom_of_pipe()}, + &.{target.bottom_of_pipe_barrier()}, ); } try cmd.endCommandBuffer(); diff --git a/src/nu/Render/SwapChain.zig b/src/nu/Render/SwapChain.zig index b2df68d..5ab0969 100644 --- a/src/nu/Render/SwapChain.zig +++ b/src/nu/Render/SwapChain.zig @@ -44,7 +44,7 @@ pub const Target = struct { 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 - pub fn top_of_pipe(target: Target) vk.ImageMemoryBarrier { + pub fn top_of_pipe_barrier(target: Target) vk.ImageMemoryBarrier { return vk.ImageMemoryBarrier{ .src_access_mask = .{}, .dst_access_mask = .{ .color_attachment_write_bit = true }, @@ -63,7 +63,7 @@ pub const Target = struct { }; } - pub fn bottom_of_pipe(target: Target) vk.ImageMemoryBarrier { + pub fn bottom_of_pipe_barrier(target: Target) vk.ImageMemoryBarrier { return vk.ImageMemoryBarrier{ .src_access_mask = .{ .color_attachment_write_bit = true }, .dst_access_mask = .{}, @@ -142,15 +142,16 @@ pub fn init(alloc: std.mem.Allocator, flight_count: usize) !Self { .old_swapchain = .null_handle, }; + // try self.rebuild(); + return self; } -fn rebuild(self: *Self) !void { - std.debug.assert(self.handle == .null_handle); // don't rebuild if we weren't marked - +pub fn rebuild(self: *Self) !void { const caps = try ctx.getPhysicalDeviceSurfaceCapabilities(); self.cinfo.image_extent = caps.current_extent; + try ctx.D.queueWaitIdle(ctx.queue.*); self.handle = try ctx.D.createSwapchainKHR(&self.cinfo, null); ctx.D.destroySwapchainKHR(self.cinfo.old_swapchain, null); self.cinfo.old_swapchain = self.handle; @@ -215,7 +216,7 @@ pub fn deinit(self: *Self) void { self.flight_syncs.deinit(self.alloc); } -pub fn acquire(self: *Self) !Target { +pub fn acquire(self: *Self) !?Target { var target: Target = .{ .flight_index = @intCast(self.flight_index), .acquired = self.flight_syncs.items(.acquired)[self.flight_index], @@ -228,74 +229,59 @@ pub fn acquire(self: *Self) !Target { const timeout = std.math.maxInt(u64); - for (0..5) |_| { - if (self.handle == .null_handle) { - try self.rebuild(); - std.debug.assert(self.handle != .null_handle); - } + std.debug.assert(.success == try ctx.D.waitForFences( + 1, + &.{target.available}, + vk.TRUE, + std.math.maxInt(u64), + )); - const fences: [1]vk.Fence = .{target.available}; - std.debug.assert(.success == try ctx.D.waitForFences( - 1, - &fences, - vk.TRUE, - std.math.maxInt(u64), - )); + // two attempts + + target.image_index = for (0..2) |_| { + if (self.handle == .null_handle) try self.rebuild(); if (ctx.D.acquireNextImageKHR( self.handle, timeout, target.acquired, .null_handle, - )) |res| { - switch (res.result) { - .success, .suboptimal_khr => {}, - else => unreachable, - } - - target.image_index = res.image_index; - target.image = self.chain.items(.image)[res.image_index]; - target.view = self.chain.items(.view)[res.image_index]; - - try ctx.D.resetFences(1, &.{target.available}); - self.flight_index = @mod(self.flight_index + 1, self.flight_syncs.len); - - return target; + )) |res| switch (res.result) { + .success, .suboptimal_khr => break res.image_index, + else => unreachable, } else |err| switch (err) { error.OutOfDateKHR => { self.handle = .null_handle; - continue; }, else => return err, } } else { - return error.CannotRecreateSwapchain; - } + return null; + }; + + target.image = self.chain.items(.image)[target.image_index]; + target.view = self.chain.items(.view)[target.image_index]; + + try ctx.D.resetFences(1, &.{target.available}); + self.flight_index = @mod(self.flight_index + 1, self.flight_syncs.len); + + return target; } pub fn present(self: *Self, target: Target) !void { if (ctx.Q.presentKHR(&vk.PresentInfoKHR{ - .wait_semaphore_count = 1, // todo extra semaphores? + .wait_semaphore_count = 1, .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; - }, + })) |res| switch(res) { + .success => {}, + .suboptimal_khr => self.handle = .null_handle, + else => unreachable, + } else |err| switch(err) { + error.OutOfDateKHR => self.handle = .null_handle, else => return err, } } diff --git a/src/nu/Render/ctx.zig b/src/nu/Render/ctx.zig index e3a2397..eb7343e 100644 --- a/src/nu/Render/ctx.zig +++ b/src/nu/Render/ctx.zig @@ -17,7 +17,9 @@ pub const versions: []const vk.ApiInfo = &.{ pub const instance_exts: []const vk.ApiInfo = if (config.use_debug_messenger) &.{ vk.extensions.ext_debug_utils, vk.extensions.khr_surface, -} else &.{}; +} else &.{ + vk.extensions.khr_surface, +}; pub const device_exts: []const vk.ApiInfo = &.{ vk.extensions.khr_swapchain, @@ -70,10 +72,16 @@ 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); + _B = if (config.use_debug_messenger) + try BaseWrapper.load(glfwGetInstanceProcAddress) + else + BaseWrapper.loadNoFail(glfwGetInstanceProcAddress); _instance = try _create_instance(alloc); - _iw = try InstanceWrapper.load(_instance, glfwGetInstanceProcAddress); + _iw = if (config.use_debug_messenger) + try InstanceWrapper.load(_instance, glfwGetInstanceProcAddress) + else + InstanceWrapper.loadNoFail(_instance, glfwGetInstanceProcAddress); errdefer _destroy_instance(); _I = InstanceProxy.init(_instance, iw); @@ -86,7 +94,10 @@ pub fn init(alloc: std.mem.Allocator) !void { _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); + _dw = if (config.use_debug_messenger) + try DeviceWrapper.load(_device, iw.dispatch.vkGetDeviceProcAddr) + else + DeviceWrapper.loadNoFail(_device, iw.dispatch.vkGetDeviceProcAddr); errdefer _destroy_device(); _D = DeviceProxy.init(_device, dw); _queue = D.getDeviceQueue(_family, 0); // only one queue supported diff --git a/src/nu/Window.zig b/src/nu/Window.zig index 3d10aed..9ae74e5 100644 --- a/src/nu/Window.zig +++ b/src/nu/Window.zig @@ -54,6 +54,8 @@ pub fn setup(_: std.mem.Allocator) !void { null, ) orelse std.debug.panic("GLFW Create Window Failed", .{}); + _ = c.glfwSetFramebufferSizeCallback(handle, &resize_callback); + // bus.connect(handle); // errdefer bus.disconnect(handle); } @@ -75,3 +77,19 @@ pub fn next() bool { return true; } + +var _resize_callbacks: std.BoundedArray(*const fn (u32, u32) void, 16) = .{}; + +fn resize_callback(_: ?*c.GLFWwindow, w: c_int, h: c_int) callconv(.C) void { + for (_resize_callbacks.slice()) |cb| { + cb(@intCast(w), @intCast(h)); + } +} + +pub fn add_resize_callback(cb: *const fn (u32, u32) void) void { + _resize_callbacks.appendAssumeCapacity(cb); +} + +pub fn set_title(title: [:0]const u8) void { + c.glfwSetWindowTitle(handle, title); +}