diff --git a/src/main.zig b/src/main.zig index 93527c8..5265ec6 100644 --- a/src/main.zig +++ b/src/main.zig @@ -28,6 +28,8 @@ pub fn main() !void { else => {}, } } + + try vulkan_renderer.draw(); } } diff --git a/src/vulkan_renderer.zig b/src/vulkan_renderer.zig index 593aec5..27db5ab 100644 --- a/src/vulkan_renderer.zig +++ b/src/vulkan_renderer.zig @@ -48,6 +48,8 @@ pub const VulkanRenderer = struct { presentation_queue: Queue, surface: vk.SurfaceKHR, swapchain: vk.SwapchainKHR, + viewport: vk.Viewport, + scissor: vk.Rect2D, swapchain_images: []SwapchainImage, swapchain_framebuffers: []vk.Framebuffer, @@ -65,6 +67,10 @@ pub const VulkanRenderer = struct { swapchain_image_format: vk.Format, extent: vk.Extent2D, + // Synchronisation + image_available: vk.Semaphore, + render_finished: vk.Semaphore, + debug_utils: ?vk.DebugUtilsMessengerEXT, pub fn init(window: sdl.Window, allocator: std.mem.Allocator) !Self { @@ -89,10 +95,82 @@ pub const VulkanRenderer = struct { try self.createFramebuffers(); try self.createCommandPool(); try self.createCommandBuffers(); + try self.recordCommands(); + try self.createSynchronisation(); return self; } + pub fn draw(self: *Self) !void { + // -- 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, .null_handle); + + // -- Submit command buffer to render + // Queue submission information + const wait_stages = [_]vk.PipelineStageFlags{.{ .color_attachment_output_bit = true }}; + + 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_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 + }; + + // Submit command buffer to queue + try self.device.queueSubmit(self.graphics_queue.handle, 1, @ptrCast(&submit_info), .null_handle); + + // -- 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 + .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 + }; + + // Present image + _ = try self.device.queuePresentKHR(self.presentation_queue.handle, &present_info); + } + + pub fn deinit(self: *Self) void { + if (enable_validation_layers) { + self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null); + } + + self.device.destroySemaphore(self.render_finished, null); + self.device.destroySemaphore(self.image_available, null); + + self.allocator.free(self.command_buffers); + self.device.destroyCommandPool(self.graphics_command_pool, null); + + for (self.swapchain_framebuffers) |framebuffer| { + self.device.destroyFramebuffer(framebuffer, null); + } + + self.allocator.free(self.swapchain_framebuffers); + + self.device.destroyPipeline(self.graphics_pipeline, null); + self.device.destroyPipelineLayout(self.pipeline_layout, null); + self.device.destroyRenderPass(self.render_pass, null); + + for (self.swapchain_images) |swapchain_image| { + self.device.destroyImageView(swapchain_image.image_view, null); + } + + self.allocator.free(self.swapchain_images); + self.device.destroySwapchainKHR(self.swapchain, null); + self.device.destroyDevice(null); + self.instance.destroySurfaceKHR(self.surface, null); + self.instance.destroyInstance(null); + + self.allocator.destroy(self.device.wrapper); + self.allocator.destroy(self.instance.wrapper); + } + fn createInstance(self: *Self) !void { if (enable_validation_layers and !self.checkValidationLayersSupport()) { // TODO Better error @@ -380,7 +458,7 @@ pub const VulkanRenderer = struct { }; // -- Viewport & scissor -- - const viewport: vk.Viewport = .{ + self.viewport = .{ .x = 0.0, .y = 0.0, .width = @floatFromInt(self.extent.width), @@ -389,16 +467,16 @@ pub const VulkanRenderer = struct { .max_depth = 1.0, }; - const scissor: vk.Rect2D = .{ + self.scissor = .{ .offset = .{ .x = 0, .y = 0 }, .extent = self.extent, }; const viewport_state_create_info: vk.PipelineViewportStateCreateInfo = .{ .viewport_count = 1, - .p_viewports = @ptrCast(&viewport), + .p_viewports = @ptrCast(&self.viewport), .scissor_count = 1, - .p_scissors = @ptrCast(&scissor), + .p_scissors = @ptrCast(&self.scissor), }; // -- Dynamic states -- @@ -549,6 +627,12 @@ 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); + } + fn recordCommands(self: *Self) !void { // Information about how to begin each command const buffer_begin_info: vk.CommandBufferBeginInfo = .{ @@ -569,6 +653,7 @@ pub const VulkanRenderer = struct { }, .p_clear_values = &clear_values, // List of clear values (TODO: Depth attachment clear value) .clear_value_count = 1, + .framebuffer = undefined, }; for (self.command_buffers, 0..) |command_buffer, i| { @@ -584,6 +669,10 @@ pub const VulkanRenderer = struct { // Bind pipeline to be used in render pass command_buffer.bindPipeline(.graphics, self.graphics_pipeline); + // Needed when using dynamic state + command_buffer.setViewport(0, 1, @ptrCast(&self.viewport)); + command_buffer.setScissor(0, 1, @ptrCast(&self.scissor)); + // Execute a pipeline command_buffer.draw(3, 1, 0, 0); @@ -806,38 +895,6 @@ pub const VulkanRenderer = struct { return try self.device.createImageView(&image_view_create_info, null); } - - pub fn deinit(self: *Self) void { - if (enable_validation_layers) { - self.instance.destroyDebugUtilsMessengerEXT(self.debug_utils.?, null); - } - - self.allocator.free(self.command_buffers); - self.device.destroyCommandPool(self.graphics_command_pool, null); - - for (self.swapchain_framebuffers) |framebuffer| { - self.device.destroyFramebuffer(framebuffer, null); - } - - self.allocator.free(self.swapchain_framebuffers); - - self.device.destroyPipeline(self.graphics_pipeline, null); - self.device.destroyPipelineLayout(self.pipeline_layout, null); - self.device.destroyRenderPass(self.render_pass, null); - - for (self.swapchain_images) |swapchain_image| { - self.device.destroyImageView(swapchain_image.image_view, null); - } - - self.allocator.free(self.swapchain_images); - self.device.destroySwapchainKHR(self.swapchain, null); - self.device.destroyDevice(null); - self.instance.destroySurfaceKHR(self.surface, null); - self.instance.destroyInstance(null); - - self.allocator.destroy(self.device.wrapper); - self.allocator.destroy(self.instance.wrapper); - } }; // Format: VK_FORMAT_R8G8B8A8_UNORM (VK_FORMAT_B8G8R8A8_UNORM as backup)