diff --git a/README.md b/README.md new file mode 100644 index 0000000..88b26ae --- /dev/null +++ b/README.md @@ -0,0 +1,11 @@ +# Vulkan renderer in Zig & SDL2 + +This is just a project to learn Vulkan using the Zig programming language. + +## Dependencies + +TODO + +## Build + +TODO diff --git a/src/vulkan_renderer.zig b/src/vulkan_renderer.zig index 0b180f9..149ce94 100644 --- a/src/vulkan_renderer.zig +++ b/src/vulkan_renderer.zig @@ -12,6 +12,8 @@ const SwapchainImage = Utilities.SwapchainImage; const enable_validation_layers = builtin.mode == .Debug; const validation_layers = [_][*:0]const u8{"VK_LAYER_KHRONOS_validation"}; +const MAX_FRAME_DRAWS: u32 = 2; + const apis: []const vk.ApiInfo = &.{ vk.features.version_1_0, vk.features.version_1_1, @@ -40,6 +42,8 @@ pub const VulkanRenderer = struct { window: sdl.Window, + current_frame: u32 = 0, + // Main instance: Instance, physical_device: vk.PhysicalDevice, @@ -68,9 +72,9 @@ pub const VulkanRenderer = struct { extent: vk.Extent2D, // Synchronisation - image_available: vk.Semaphore, - render_finished: vk.Semaphore, - frame_fence: vk.Fence, + image_available: [MAX_FRAME_DRAWS]vk.Semaphore, + render_finished: [MAX_FRAME_DRAWS]vk.Semaphore, + draw_fences: [MAX_FRAME_DRAWS]vk.Fence, debug_utils: ?vk.DebugUtilsMessengerEXT, @@ -78,6 +82,7 @@ pub const VulkanRenderer = struct { var self: Self = undefined; self.window = window; + self.current_frame = 0; self.allocator = allocator; self.vkb = try BaseDispatch.load(try sdl.vulkan.getVkGetInstanceProcAddr()); @@ -103,14 +108,17 @@ pub const VulkanRenderer = struct { } pub fn draw(self: *Self) !void { - _ = try self.device.waitForFences(1, @ptrCast(&self.frame_fence), vk.TRUE, std.math.maxInt(u64)); - _ = try self.device.resetFences(1, @ptrCast(&self.frame_fence)); + // Wait for given fence to signal (open) from last draw before continuing + _ = try self.device.waitForFences(1, @ptrCast(&self.draw_fences[self.current_frame]), vk.TRUE, std.math.maxInt(u64)); + // Manually reset (close) fences + try self.device.resetFences(1, @ptrCast(&self.draw_fences[self.current_frame])); + // -- Get next image // Get index of next image to be drawn to, and signal semaphore when ready to be drawn to const image_index_result = try self.device.acquireNextImageKHR( self.swapchain, std.math.maxInt(u64), - self.image_available, + self.image_available[self.current_frame], .null_handle, ); @@ -120,21 +128,22 @@ pub const VulkanRenderer = struct { const submit_info: vk.SubmitInfo = .{ .wait_semaphore_count = 1, // Number of semaphores to wait on - .p_wait_semaphores = @ptrCast(&self.image_available), // List of semaphores to wait on + .p_wait_semaphores = @ptrCast(&self.image_available[self.current_frame]), // List of semaphores to wait on .p_wait_dst_stage_mask = &wait_stages, // Stages to check semaphores at .command_buffer_count = 1, // Number of command buffers to submit .p_command_buffers = @ptrCast(&self.command_buffers[image_index_result.image_index]), // Command buffer to submit .signal_semaphore_count = 1, // Number of semaphores to signal - .p_signal_semaphores = @ptrCast(&self.render_finished), // List of semaphores to signal when command buffer finishes + .p_signal_semaphores = @ptrCast(&self.render_finished[self.current_frame]), // List of semaphores to signal when command buffer finishes }; // Submit command buffer to queue - try self.device.queueSubmit(self.graphics_queue.handle, 1, @ptrCast(&submit_info), self.frame_fence); + try self.device.queueSubmit(self.graphics_queue.handle, 1, @ptrCast(&submit_info), self.draw_fences[self.current_frame]); + // try self.device.queueSubmit(self.graphics_queue.handle, 1, @ptrCast(&submit_info), self.frame_fence); // -- Present rendered image to screen const present_info: vk.PresentInfoKHR = .{ .wait_semaphore_count = 1, // Number of semaphores to wait on - .p_wait_semaphores = @ptrCast(&self.render_finished), // Semaphores to wait on + .p_wait_semaphores = @ptrCast(&self.render_finished[self.current_frame]), // Semaphores to wait on .swapchain_count = 1, // Number of swapchains to present to .p_swapchains = @ptrCast(&self.swapchain), // Swapchains to present images to .p_image_indices = @ptrCast(&image_index_result.image_index), // Index of images in swapchains to present @@ -142,6 +151,9 @@ pub const VulkanRenderer = struct { // Present image _ = try self.device.queuePresentKHR(self.presentation_queue.handle, &present_info); + + // Get next frame (use % to keep the current frame below MAX_FRAME_DRAWS) + self.current_frame = (self.current_frame + 1) % MAX_FRAME_DRAWS; } pub fn deinit(self: *Self) void { @@ -149,10 +161,13 @@ pub const VulkanRenderer = struct { self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null); } - _ = self.device.waitForFences(1, @ptrCast(&self.frame_fence), vk.TRUE, std.math.maxInt(u64)) catch undefined; - self.device.destroyFence(self.frame_fence, null); - self.device.destroySemaphore(self.render_finished, null); - self.device.destroySemaphore(self.image_available, null); + self.device.deviceWaitIdle() catch undefined; + + for (0..MAX_FRAME_DRAWS) |i| { + self.device.destroySemaphore(self.render_finished[i], null); + self.device.destroySemaphore(self.image_available[i], null); + self.device.destroyFence(self.draw_fences[i], null); + } self.allocator.free(self.command_buffers); self.device.destroyCommandPool(self.graphics_command_pool, null); @@ -638,12 +653,15 @@ pub const VulkanRenderer = struct { } fn createSynchronisation(self: *Self) !void { - // Semaphore creation information - self.image_available = try self.device.createSemaphore(&.{}, null); - self.render_finished = try self.device.createSemaphore(&.{}, null); - + // Fence create information const fence_create_info: vk.FenceCreateInfo = .{ .flags = .{ .signaled_bit = true } }; - self.frame_fence = try self.device.createFence(&fence_create_info, null); + + // Semaphore creation information + for (0..MAX_FRAME_DRAWS) |i| { + self.image_available[i] = try self.device.createSemaphore(&.{}, null); + self.render_finished[i] = try self.device.createSemaphore(&.{}, null); + self.draw_fences[i] = try self.device.createFence(&fence_create_info, null); + } } fn recordCommands(self: *Self) !void {